From 1ee47cec31c4f3f72679da907216c127be094778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Fri, 22 Sep 2017 23:28:30 -0500 Subject: [PATCH 1/2] Support location check with vendor placeholder Update location check logic triggered if `flyway.check-location=true` to resolve any vendor placeholders in `flyway.locations`. See gh-10387 --- .../flyway/FlywayAutoConfiguration.java | 108 +++++++++--------- .../flyway/FlywayAutoConfigurationTests.java | 11 ++ 2 files changed, 68 insertions(+), 51 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java index f9a16c04e65..1f16fd868f1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java @@ -16,12 +16,12 @@ package org.springframework.boot.autoconfigure.flyway; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.annotation.PostConstruct; import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @@ -104,6 +104,8 @@ public class FlywayAutoConfiguration { private List flywayCallbacks; + private static final String VENDOR_PLACEHOLDER = "{vendor}"; + public FlywayConfiguration(FlywayProperties properties, ResourceLoader resourceLoader, ObjectProvider dataSource, @FlywayDataSource ObjectProvider flywayDataSource, @@ -117,28 +119,6 @@ public class FlywayAutoConfiguration { 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 @ConfigurationProperties(prefix = "spring.flyway") public Flyway flyway() { @@ -156,10 +136,61 @@ public class FlywayAutoConfiguration { } flyway.setCallbacks(this.flywayCallbacks .toArray(new FlywayCallback[this.flywayCallbacks.size()])); - flyway.setLocations(this.properties.getLocations().toArray(new String[0])); + String[] locations = resolveLocations(this.properties.getLocations().toArray(new String[0]), flyway.getDataSource()); + checkLocationExists(locations); + flyway.setLocations(locations); return flyway; } + private static String[] resolveLocations(String[] locations, DataSource dataSource) { + if (usesVendorLocation(locations)) { + try { + String url = (String) JdbcUtils + .extractDatabaseMetaData(dataSource, "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); + } + } + return locations; + } + + private static boolean usesVendorLocation(String... locations) { + for (String location : locations) { + if (location.contains(VENDOR_PLACEHOLDER)) { + return true; + } + } + return false; + } + + 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 @ConditionalOnMissingBean public FlywayMigrationInitializer flywayInitializer(Flyway flyway) { @@ -202,38 +233,13 @@ public class FlywayAutoConfiguration { private static class SpringBootFlyway extends Flyway { - private static final String VENDOR_PLACEHOLDER = "{vendor}"; - @Override public void setLocations(String... locations) { - if (usesVendorLocation(locations)) { - try { - String url = (String) JdbcUtils - .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); - } - } + locations = FlywayConfiguration.resolveLocations(locations, + getDataSource()); super.setLocations(locations); } - private boolean usesVendorLocation(String... locations) { - for (String location : locations) { - if (location.contains(VENDOR_PLACEHOLDER)) { - return true; - } - } - return false; - } - } /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java index fbc279c0b86..8d8006350cf 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java @@ -255,6 +255,17 @@ 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 public void callbacksAreConfiguredAndOrdered() { this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, From 72862b565ce507c75efb978a5bbe235e5f2094bc Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sat, 18 Nov 2017 20:29:43 -0800 Subject: [PATCH 2/2] Polish location check with vendor placeholder Closes gh-10387 --- .../flyway/FlywayAutoConfiguration.java | 101 +++++++++++------- .../flyway/FlywayAutoConfigurationTests.java | 6 +- 2 files changed, 68 insertions(+), 39 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java index 1f16fd868f1..dc2802431b4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.flyway; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -104,8 +105,6 @@ public class FlywayAutoConfiguration { private List flywayCallbacks; - private static final String VENDOR_PLACEHOLDER = "{vendor}"; - public FlywayConfiguration(FlywayProperties properties, ResourceLoader resourceLoader, ObjectProvider dataSource, @FlywayDataSource ObjectProvider flywayDataSource, @@ -136,49 +135,22 @@ public class FlywayAutoConfiguration { } flyway.setCallbacks(this.flywayCallbacks .toArray(new FlywayCallback[this.flywayCallbacks.size()])); - String[] locations = resolveLocations(this.properties.getLocations().toArray(new String[0]), flyway.getDataSource()); + String[] locations = new LocationResolver(flyway.getDataSource()) + .resolveLocations(this.properties.getLocations()); checkLocationExists(locations); flyway.setLocations(locations); return flyway; } - private static String[] resolveLocations(String[] locations, DataSource dataSource) { - if (usesVendorLocation(locations)) { - try { - String url = (String) JdbcUtils - .extractDatabaseMetaData(dataSource, "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); - } - } - return locations; - } - - private static boolean usesVendorLocation(String... locations) { - for (String location : locations) { - if (location.contains(VENDOR_PLACEHOLDER)) { - return true; - } - } - return false; - } - 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)"); + () -> "Cannot find migrations location in: " + Arrays.asList( + locations) + + " (please add migrations or check your Flyway configuration)"); } } @@ -235,9 +207,64 @@ public class FlywayAutoConfiguration { @Override public void setLocations(String... locations) { - locations = FlywayConfiguration.resolveLocations(locations, - getDataSource()); - super.setLocations(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 locations) { + return resolveLocations(locations.toArray(new String[locations.size()])); + } + + public String[] resolveLocations(String[] locations) { + if (usesVendorLocation(locations)) { + DatabaseDriver databaseDriver = getDatabaseDriver(); + return replaceVendorLocations(locations, databaseDriver); + } + 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) { + for (String location : locations) { + if (location.contains(VENDOR_PLACEHOLDER)) { + return true; + } + } + return false; } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java index 8d8006350cf..da365536f76 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java @@ -258,11 +258,13 @@ public class FlywayAutoConfigurationTests { @Test public void useOneLocationWithVendorDirectory() { this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) - .withPropertyValues("spring.flyway.locations=classpath:db/vendors/{vendor}") + .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"); + assertThat(flyway.getLocations()) + .containsExactly("classpath:db/vendors/h2"); }); }