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
This commit is contained in:
		
						commit
						d1dc8cb72a
					
				| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +22,7 @@ import java.util.Collections;
 | 
			
		|||
import java.util.HashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.function.Supplier;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.EntityManagerFactory;
 | 
			
		||||
import javax.sql.DataSource;
 | 
			
		||||
| 
						 | 
				
			
			@ -39,6 +40,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
 | 
			
		|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 | 
			
		||||
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.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
 | 
			
		||||
| 
						 | 
				
			
			@ -65,6 +67,7 @@ import org.springframework.util.ObjectUtils;
 | 
			
		|||
 * @author Stephane Nicoll
 | 
			
		||||
 * @author Jacques-Etienne Beaudet
 | 
			
		||||
 * @author Eddú Meléndez
 | 
			
		||||
 * @author Dominic Gunn
 | 
			
		||||
 * @since 1.1.0
 | 
			
		||||
 */
 | 
			
		||||
@Configuration
 | 
			
		||||
| 
						 | 
				
			
			@ -95,6 +98,8 @@ public class FlywayAutoConfiguration {
 | 
			
		|||
 | 
			
		||||
		private final FlywayProperties properties;
 | 
			
		||||
 | 
			
		||||
		private final DataSourceProperties dataSourceProperties;
 | 
			
		||||
 | 
			
		||||
		private final ResourceLoader resourceLoader;
 | 
			
		||||
 | 
			
		||||
		private final DataSource dataSource;
 | 
			
		||||
| 
						 | 
				
			
			@ -106,11 +111,13 @@ public class FlywayAutoConfiguration {
 | 
			
		|||
		private List<FlywayCallback> flywayCallbacks;
 | 
			
		||||
 | 
			
		||||
		public FlywayConfiguration(FlywayProperties properties,
 | 
			
		||||
				ResourceLoader resourceLoader, ObjectProvider<DataSource> dataSource,
 | 
			
		||||
				DataSourceProperties dataSourceProperties, ResourceLoader resourceLoader,
 | 
			
		||||
				ObjectProvider<DataSource> dataSource,
 | 
			
		||||
				@FlywayDataSource ObjectProvider<DataSource> flywayDataSource,
 | 
			
		||||
				ObjectProvider<FlywayMigrationStrategy> migrationStrategy,
 | 
			
		||||
				ObjectProvider<List<FlywayCallback>> flywayCallbacks) {
 | 
			
		||||
			this.properties = properties;
 | 
			
		||||
			this.dataSourceProperties = dataSourceProperties;
 | 
			
		||||
			this.resourceLoader = resourceLoader;
 | 
			
		||||
			this.dataSource = dataSource.getIfUnique();
 | 
			
		||||
			this.flywayDataSource = flywayDataSource.getIfAvailable();
 | 
			
		||||
| 
						 | 
				
			
			@ -123,8 +130,13 @@ public class FlywayAutoConfiguration {
 | 
			
		|||
		public Flyway flyway() {
 | 
			
		||||
			Flyway flyway = new SpringBootFlyway();
 | 
			
		||||
			if (this.properties.isCreateDataSource()) {
 | 
			
		||||
				flyway.setDataSource(this.properties.getUrl(), this.properties.getUser(),
 | 
			
		||||
						this.properties.getPassword(),
 | 
			
		||||
				String url = getProperty(this.properties::getUrl,
 | 
			
		||||
						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]));
 | 
			
		||||
			}
 | 
			
		||||
			else if (this.flywayDataSource != null) {
 | 
			
		||||
| 
						 | 
				
			
			@ -142,6 +154,12 @@ public class FlywayAutoConfiguration {
 | 
			
		|||
			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) {
 | 
			
		||||
			if (this.properties.isCheckLocation()) {
 | 
			
		||||
				Assert.state(locations.length != 0,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
| 
						 | 
				
			
			@ -133,7 +133,7 @@ public class FlywayProperties {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		|||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.function.Supplier;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
import javax.persistence.EntityManagerFactory;
 | 
			
		||||
| 
						 | 
				
			
			@ -37,6 +38,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
 | 
			
		|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 | 
			
		||||
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.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
 | 
			
		||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
 | 
			
		||||
import org.springframework.boot.jdbc.DataSourceBuilder;
 | 
			
		||||
| 
						 | 
				
			
			@ -58,6 +60,7 @@ import org.springframework.util.ReflectionUtils;
 | 
			
		|||
 * @author Phillip Webb
 | 
			
		||||
 * @author Eddú Meléndez
 | 
			
		||||
 * @author Andy Wilkinson
 | 
			
		||||
 * @author Dominic Gunn
 | 
			
		||||
 * @since 1.1.0
 | 
			
		||||
 */
 | 
			
		||||
@Configuration
 | 
			
		||||
| 
						 | 
				
			
			@ -83,6 +86,8 @@ public class LiquibaseAutoConfiguration {
 | 
			
		|||
 | 
			
		||||
		private final LiquibaseProperties properties;
 | 
			
		||||
 | 
			
		||||
		private final DataSourceProperties dataSourceProperties;
 | 
			
		||||
 | 
			
		||||
		private final ResourceLoader resourceLoader;
 | 
			
		||||
 | 
			
		||||
		private final DataSource dataSource;
 | 
			
		||||
| 
						 | 
				
			
			@ -90,9 +95,11 @@ public class LiquibaseAutoConfiguration {
 | 
			
		|||
		private final DataSource liquibaseDataSource;
 | 
			
		||||
 | 
			
		||||
		public LiquibaseConfiguration(LiquibaseProperties properties,
 | 
			
		||||
				ResourceLoader resourceLoader, ObjectProvider<DataSource> dataSource,
 | 
			
		||||
				DataSourceProperties dataSourceProperties, ResourceLoader resourceLoader,
 | 
			
		||||
				ObjectProvider<DataSource> dataSource,
 | 
			
		||||
				@LiquibaseDataSource ObjectProvider<DataSource> liquibaseDataSource) {
 | 
			
		||||
			this.properties = properties;
 | 
			
		||||
			this.dataSourceProperties = dataSourceProperties;
 | 
			
		||||
			this.resourceLoader = resourceLoader;
 | 
			
		||||
			this.dataSource = dataSource.getIfUnique();
 | 
			
		||||
			this.liquibaseDataSource = liquibaseDataSource.getIfAvailable();
 | 
			
		||||
| 
						 | 
				
			
			@ -140,16 +147,27 @@ public class LiquibaseAutoConfiguration {
 | 
			
		|||
			if (this.liquibaseDataSource != null) {
 | 
			
		||||
				return this.liquibaseDataSource;
 | 
			
		||||
			}
 | 
			
		||||
			if (this.properties.getUrl() == null) {
 | 
			
		||||
			if (this.properties.getUrl() == null && this.properties.getUser() == null) {
 | 
			
		||||
				return this.dataSource;
 | 
			
		||||
			}
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private DataSource createNewDataSource() {
 | 
			
		||||
			return DataSourceBuilder.create().url(this.properties.getUrl())
 | 
			
		||||
					.username(this.properties.getUser())
 | 
			
		||||
					.password(this.properties.getPassword()).build();
 | 
			
		||||
			String url = getProperty(this.properties::getUrl,
 | 
			
		||||
					this.dataSourceProperties::getUrl);
 | 
			
		||||
			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;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
| 
						 | 
				
			
			@ -60,6 +60,7 @@ import static org.mockito.Mockito.mock;
 | 
			
		|||
 * @author Vedran Pavic
 | 
			
		||||
 * @author Eddú Meléndez
 | 
			
		||||
 * @author Stephane Nicoll
 | 
			
		||||
 * @author Dominic Gunn
 | 
			
		||||
 */
 | 
			
		||||
public class FlywayAutoConfigurationTests {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -74,9 +75,19 @@ public class FlywayAutoConfigurationTests {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void createDataSource() {
 | 
			
		||||
	public void createDataSourceWithUrl() {
 | 
			
		||||
		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")
 | 
			
		||||
				.run((context) -> {
 | 
			
		||||
					assertThat(context).hasSingleBean(Flyway.class);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,6 +58,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		|||
 * @author Eddú Meléndez
 | 
			
		||||
 * @author Andy Wilkinson
 | 
			
		||||
 * @author Stephane Nicoll
 | 
			
		||||
 * @author Dominic Gunn
 | 
			
		||||
 */
 | 
			
		||||
public class LiquibaseAutoConfigurationTests {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -153,8 +154,7 @@ public class LiquibaseAutoConfigurationTests {
 | 
			
		|||
	@Test
 | 
			
		||||
	public void overrideDataSource() {
 | 
			
		||||
		this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
 | 
			
		||||
				.withPropertyValues("spring.liquibase.url:jdbc:hsqldb:mem:liquibase",
 | 
			
		||||
						"spring.liquibase.user:sa")
 | 
			
		||||
				.withPropertyValues("spring.liquibase.url:jdbc:hsqldb:mem:liquibase")
 | 
			
		||||
				.run(assertLiquibase((liquibase) -> {
 | 
			
		||||
					DataSource dataSource = liquibase.getDataSource();
 | 
			
		||||
					assertThat(((HikariDataSource) dataSource).isClosed()).isTrue();
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
	public void changeLogDoesNotExist() {
 | 
			
		||||
		this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
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]`
 | 
			
		||||
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
 | 
			
		||||
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
 | 
			
		|||
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
 | 
			
		||||
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
 | 
			
		||||
{sc-spring-boot-autoconfigure}/liquibase/LiquibaseProperties.{sc-ext}[`LiquibaseProperties`]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue