Merge pull request #10387 from eddumelendez/flyway_check_vendor_locations
* pr/10387: Polish location check with vendor placeholder Support location check with vendor placeholder
This commit is contained in:
		
						commit
						bc322ff593
					
				| 
						 | 
					@ -16,12 +16,13 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package org.springframework.boot.autoconfigure.flyway;
 | 
					package org.springframework.boot.autoconfigure.flyway;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					import java.util.Collection;
 | 
				
			||||||
import java.util.Collections;
 | 
					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 javax.annotation.PostConstruct;
 | 
					 | 
				
			||||||
import javax.persistence.EntityManagerFactory;
 | 
					import javax.persistence.EntityManagerFactory;
 | 
				
			||||||
import javax.sql.DataSource;
 | 
					import javax.sql.DataSource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -117,28 +118,6 @@ public class FlywayAutoConfiguration {
 | 
				
			||||||
			this.flywayCallbacks = flywayCallbacks.getIfAvailable(Collections::emptyList);
 | 
								this.flywayCallbacks = flywayCallbacks.getIfAvailable(Collections::emptyList);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		@PostConstruct
 | 
					 | 
				
			||||||
		public void checkLocationExists() {
 | 
					 | 
				
			||||||
			if (this.properties.isCheckLocation()) {
 | 
					 | 
				
			||||||
				Assert.state(!this.properties.getLocations().isEmpty(),
 | 
					 | 
				
			||||||
						"Migration script locations not configured");
 | 
					 | 
				
			||||||
				boolean exists = hasAtLeastOneLocation();
 | 
					 | 
				
			||||||
				Assert.state(exists,
 | 
					 | 
				
			||||||
						() -> "Cannot find migrations location in: " + this.properties
 | 
					 | 
				
			||||||
								.getLocations()
 | 
					 | 
				
			||||||
						+ " (please add migrations or check your Flyway configuration)");
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		private boolean hasAtLeastOneLocation() {
 | 
					 | 
				
			||||||
			for (String location : this.properties.getLocations()) {
 | 
					 | 
				
			||||||
				if (this.resourceLoader.getResource(location).exists()) {
 | 
					 | 
				
			||||||
					return true;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		@Bean
 | 
							@Bean
 | 
				
			||||||
		@ConfigurationProperties(prefix = "spring.flyway")
 | 
							@ConfigurationProperties(prefix = "spring.flyway")
 | 
				
			||||||
		public Flyway flyway() {
 | 
							public Flyway flyway() {
 | 
				
			||||||
| 
						 | 
					@ -156,10 +135,34 @@ public class FlywayAutoConfiguration {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			flyway.setCallbacks(this.flywayCallbacks
 | 
								flyway.setCallbacks(this.flywayCallbacks
 | 
				
			||||||
					.toArray(new FlywayCallback[this.flywayCallbacks.size()]));
 | 
										.toArray(new FlywayCallback[this.flywayCallbacks.size()]));
 | 
				
			||||||
			flyway.setLocations(this.properties.getLocations().toArray(new String[0]));
 | 
								String[] locations = new LocationResolver(flyway.getDataSource())
 | 
				
			||||||
 | 
										.resolveLocations(this.properties.getLocations());
 | 
				
			||||||
 | 
								checkLocationExists(locations);
 | 
				
			||||||
 | 
								flyway.setLocations(locations);
 | 
				
			||||||
			return flyway;
 | 
								return flyway;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private void checkLocationExists(String... locations) {
 | 
				
			||||||
 | 
								if (this.properties.isCheckLocation()) {
 | 
				
			||||||
 | 
									Assert.state(locations.length != 0,
 | 
				
			||||||
 | 
											"Migration script locations not configured");
 | 
				
			||||||
 | 
									boolean exists = hasAtLeastOneLocation(locations);
 | 
				
			||||||
 | 
									Assert.state(exists,
 | 
				
			||||||
 | 
											() -> "Cannot find migrations location in: " + Arrays.asList(
 | 
				
			||||||
 | 
													locations)
 | 
				
			||||||
 | 
											+ " (please add migrations or check your Flyway configuration)");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private boolean hasAtLeastOneLocation(String... locations) {
 | 
				
			||||||
 | 
								for (String location : locations) {
 | 
				
			||||||
 | 
									if (this.resourceLoader.getResource(location).exists()) {
 | 
				
			||||||
 | 
										return true;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		@Bean
 | 
							@Bean
 | 
				
			||||||
		@ConditionalOnMissingBean
 | 
							@ConditionalOnMissingBean
 | 
				
			||||||
		public FlywayMigrationInitializer flywayInitializer(Flyway flyway) {
 | 
							public FlywayMigrationInitializer flywayInitializer(Flyway flyway) {
 | 
				
			||||||
| 
						 | 
					@ -202,27 +205,57 @@ public class FlywayAutoConfiguration {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static class SpringBootFlyway extends Flyway {
 | 
						private static class SpringBootFlyway extends Flyway {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private static final String VENDOR_PLACEHOLDER = "{vendor}";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		@Override
 | 
							@Override
 | 
				
			||||||
		public void setLocations(String... locations) {
 | 
							public void setLocations(String... locations) {
 | 
				
			||||||
 | 
								super.setLocations(
 | 
				
			||||||
 | 
										new LocationResolver(getDataSource()).resolveLocations(locations));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static class LocationResolver {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private static final String VENDOR_PLACEHOLDER = "{vendor}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private final DataSource dataSource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public LocationResolver(DataSource dataSource) {
 | 
				
			||||||
 | 
								this.dataSource = dataSource;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public String[] resolveLocations(Collection<String> locations) {
 | 
				
			||||||
 | 
								return resolveLocations(locations.toArray(new String[locations.size()]));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public String[] resolveLocations(String[] locations) {
 | 
				
			||||||
			if (usesVendorLocation(locations)) {
 | 
								if (usesVendorLocation(locations)) {
 | 
				
			||||||
				try {
 | 
									DatabaseDriver databaseDriver = getDatabaseDriver();
 | 
				
			||||||
					String url = (String) JdbcUtils
 | 
									return replaceVendorLocations(locations, databaseDriver);
 | 
				
			||||||
							.extractDatabaseMetaData(getDataSource(), "getURL");
 | 
					 | 
				
			||||||
					DatabaseDriver vendor = DatabaseDriver.fromJdbcUrl(url);
 | 
					 | 
				
			||||||
					if (vendor != DatabaseDriver.UNKNOWN) {
 | 
					 | 
				
			||||||
						for (int i = 0; i < locations.length; i++) {
 | 
					 | 
				
			||||||
							locations[i] = locations[i].replace(VENDOR_PLACEHOLDER,
 | 
					 | 
				
			||||||
									vendor.getId());
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				catch (MetaDataAccessException ex) {
 | 
					 | 
				
			||||||
					throw new IllegalStateException(ex);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			super.setLocations(locations);
 | 
								return locations;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private String[] replaceVendorLocations(String[] locations,
 | 
				
			||||||
 | 
									DatabaseDriver databaseDriver) {
 | 
				
			||||||
 | 
								if (databaseDriver == DatabaseDriver.UNKNOWN) {
 | 
				
			||||||
 | 
									return locations;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								String vendor = databaseDriver.getId();
 | 
				
			||||||
 | 
								return Arrays.stream(locations)
 | 
				
			||||||
 | 
										.map((location) -> location.replace(VENDOR_PLACEHOLDER, vendor))
 | 
				
			||||||
 | 
										.toArray(String[]::new);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private DatabaseDriver getDatabaseDriver() {
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									String url = (String) JdbcUtils.extractDatabaseMetaData(this.dataSource,
 | 
				
			||||||
 | 
											"getURL");
 | 
				
			||||||
 | 
									return DatabaseDriver.fromJdbcUrl(url);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								catch (MetaDataAccessException ex) {
 | 
				
			||||||
 | 
									throw new IllegalStateException(ex);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private boolean usesVendorLocation(String... locations) {
 | 
							private boolean usesVendorLocation(String... locations) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -255,6 +255,19 @@ public class FlywayAutoConfigurationTests {
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void useOneLocationWithVendorDirectory() {
 | 
				
			||||||
 | 
							this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
 | 
				
			||||||
 | 
									.withPropertyValues(
 | 
				
			||||||
 | 
											"spring.flyway.locations=classpath:db/vendors/{vendor}")
 | 
				
			||||||
 | 
									.run((context) -> {
 | 
				
			||||||
 | 
										assertThat(context).hasSingleBean(Flyway.class);
 | 
				
			||||||
 | 
										Flyway flyway = context.getBean(Flyway.class);
 | 
				
			||||||
 | 
										assertThat(flyway.getLocations())
 | 
				
			||||||
 | 
												.containsExactly("classpath:db/vendors/h2");
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void callbacksAreConfiguredAndOrdered() {
 | 
						public void callbacksAreConfiguredAndOrdered() {
 | 
				
			||||||
		this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class,
 | 
							this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue