Skip to content

Commit be93635

Browse files
committed
Fix check vendor location in FlywayAutoConfiguration
`flyway.locations` supports vendor resolution. But, if `flyway.check-location=true` is added and there is just one location with vendor location the current implementation fails. This commit adds validation for vendor locations.
1 parent 07462be commit be93635

File tree

2 files changed

+68
-51
lines changed

2 files changed

+68
-51
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java

Lines changed: 57 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616

1717
package org.springframework.boot.autoconfigure.flyway;
1818

19+
import java.util.Arrays;
1920
import java.util.Collections;
2021
import java.util.HashSet;
2122
import java.util.List;
2223
import java.util.Set;
2324

24-
import javax.annotation.PostConstruct;
2525
import javax.persistence.EntityManagerFactory;
2626
import javax.sql.DataSource;
2727

@@ -104,6 +104,8 @@ public static class FlywayConfiguration {
104104

105105
private List<FlywayCallback> flywayCallbacks;
106106

107+
private static final String VENDOR_PLACEHOLDER = "{vendor}";
108+
107109
public FlywayConfiguration(FlywayProperties properties,
108110
ResourceLoader resourceLoader, ObjectProvider<DataSource> dataSource,
109111
@FlywayDataSource ObjectProvider<DataSource> flywayDataSource,
@@ -117,28 +119,6 @@ public FlywayConfiguration(FlywayProperties properties,
117119
this.flywayCallbacks = flywayCallbacks.getIfAvailable(Collections::emptyList);
118120
}
119121

120-
@PostConstruct
121-
public void checkLocationExists() {
122-
if (this.properties.isCheckLocation()) {
123-
Assert.state(!this.properties.getLocations().isEmpty(),
124-
"Migration script locations not configured");
125-
boolean exists = hasAtLeastOneLocation();
126-
Assert.state(exists,
127-
() -> "Cannot find migrations location in: " + this.properties
128-
.getLocations()
129-
+ " (please add migrations or check your Flyway configuration)");
130-
}
131-
}
132-
133-
private boolean hasAtLeastOneLocation() {
134-
for (String location : this.properties.getLocations()) {
135-
if (this.resourceLoader.getResource(location).exists()) {
136-
return true;
137-
}
138-
}
139-
return false;
140-
}
141-
142122
@Bean
143123
@ConfigurationProperties(prefix = "spring.flyway")
144124
public Flyway flyway() {
@@ -156,10 +136,61 @@ else if (this.flywayDataSource != null) {
156136
}
157137
flyway.setCallbacks(this.flywayCallbacks
158138
.toArray(new FlywayCallback[this.flywayCallbacks.size()]));
159-
flyway.setLocations(this.properties.getLocations().toArray(new String[0]));
139+
String[] locations = resolveLocations(this.properties.getLocations().toArray(new String[0]), flyway.getDataSource());
140+
checkLocationExists(locations);
141+
flyway.setLocations(locations);
160142
return flyway;
161143
}
162144

145+
private static String[] resolveLocations(String[] locations, DataSource dataSource) {
146+
if (usesVendorLocation(locations)) {
147+
try {
148+
String url = (String) JdbcUtils
149+
.extractDatabaseMetaData(dataSource, "getURL");
150+
DatabaseDriver vendor = DatabaseDriver.fromJdbcUrl(url);
151+
if (vendor != DatabaseDriver.UNKNOWN) {
152+
for (int i = 0; i < locations.length; i++) {
153+
locations[i] = locations[i].replace(VENDOR_PLACEHOLDER,
154+
vendor.getId());
155+
}
156+
}
157+
}
158+
catch (MetaDataAccessException ex) {
159+
throw new IllegalStateException(ex);
160+
}
161+
}
162+
return locations;
163+
}
164+
165+
private static boolean usesVendorLocation(String... locations) {
166+
for (String location : locations) {
167+
if (location.contains(VENDOR_PLACEHOLDER)) {
168+
return true;
169+
}
170+
}
171+
return false;
172+
}
173+
174+
private void checkLocationExists(String... locations) {
175+
if (this.properties.isCheckLocation()) {
176+
Assert.state(locations.length != 0,
177+
"Migration script locations not configured");
178+
boolean exists = hasAtLeastOneLocation(locations);
179+
Assert.state(exists,
180+
() -> "Cannot find migrations location in: " + Arrays.asList(locations)
181+
+ " (please add migrations or check your Flyway configuration)");
182+
}
183+
}
184+
185+
private boolean hasAtLeastOneLocation(String... locations) {
186+
for (String location : locations) {
187+
if (this.resourceLoader.getResource(location).exists()) {
188+
return true;
189+
}
190+
}
191+
return false;
192+
}
193+
163194
@Bean
164195
@ConditionalOnMissingBean
165196
public FlywayMigrationInitializer flywayInitializer(Flyway flyway) {
@@ -202,38 +233,13 @@ public FlywayJpaDependencyConfiguration() {
202233

203234
private static class SpringBootFlyway extends Flyway {
204235

205-
private static final String VENDOR_PLACEHOLDER = "{vendor}";
206-
207236
@Override
208237
public void setLocations(String... locations) {
209-
if (usesVendorLocation(locations)) {
210-
try {
211-
String url = (String) JdbcUtils
212-
.extractDatabaseMetaData(getDataSource(), "getURL");
213-
DatabaseDriver vendor = DatabaseDriver.fromJdbcUrl(url);
214-
if (vendor != DatabaseDriver.UNKNOWN) {
215-
for (int i = 0; i < locations.length; i++) {
216-
locations[i] = locations[i].replace(VENDOR_PLACEHOLDER,
217-
vendor.getId());
218-
}
219-
}
220-
}
221-
catch (MetaDataAccessException ex) {
222-
throw new IllegalStateException(ex);
223-
}
224-
}
238+
locations = FlywayConfiguration.resolveLocations(locations,
239+
getDataSource());
225240
super.setLocations(locations);
226241
}
227242

228-
private boolean usesVendorLocation(String... locations) {
229-
for (String location : locations) {
230-
if (location.contains(VENDOR_PLACEHOLDER)) {
231-
return true;
232-
}
233-
}
234-
return false;
235-
}
236-
237243
}
238244

239245
/**

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,17 @@ public void useVendorDirectory() {
255255
});
256256
}
257257

258+
@Test
259+
public void useOneLocationWithVendorDirectory() {
260+
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
261+
.withPropertyValues("spring.flyway.locations=classpath:db/vendors/{vendor}")
262+
.run((context) -> {
263+
assertThat(context).hasSingleBean(Flyway.class);
264+
Flyway flyway = context.getBean(Flyway.class);
265+
assertThat(flyway.getLocations()).containsExactly("classpath:db/vendors/h2");
266+
});
267+
}
268+
258269
@Test
259270
public void callbacksAreConfiguredAndOrdered() {
260271
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class,

0 commit comments

Comments
 (0)