Skip to content

Commit 2ad12fd

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 8db77a6 commit 2ad12fd

File tree

2 files changed

+80
-63
lines changed

2 files changed

+80
-63
lines changed

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

Lines changed: 69 additions & 63 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,32 +119,25 @@ 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() {
145-
Flyway flyway = new SpringBootFlyway();
125+
Flyway flyway = createFlyway();
126+
flyway.setCallbacks(this.flywayCallbacks
127+
.toArray(new FlywayCallback[this.flywayCallbacks.size()]));
128+
String[] locations = resolveLocations(flyway.getDataSource());
129+
checkLocationExists(locations);
130+
flyway.setLocations(locations);
131+
return flyway;
132+
}
133+
134+
private Flyway createFlyway() {
135+
Flyway flyway = new Flyway() {
136+
@Override
137+
public void setLocations(String... locations) {
138+
super.setLocations(resolveLocations(getDataSource()));
139+
}
140+
};
146141
if (this.properties.isCreateDataSource()) {
147142
flyway.setDataSource(this.properties.getUrl(), this.properties.getUser(),
148143
this.properties.getPassword(),
@@ -154,12 +149,59 @@ else if (this.flywayDataSource != null) {
154149
else {
155150
flyway.setDataSource(this.dataSource);
156151
}
157-
flyway.setCallbacks(this.flywayCallbacks
158-
.toArray(new FlywayCallback[this.flywayCallbacks.size()]));
159-
flyway.setLocations(this.properties.getLocations().toArray(new String[0]));
160152
return flyway;
161153
}
162154

155+
private String[] resolveLocations(DataSource dataSource) {
156+
String[] locations = this.properties.getLocations().toArray(new String[0]);
157+
if (usesVendorLocation(locations)) {
158+
try {
159+
String url = (String) JdbcUtils
160+
.extractDatabaseMetaData(dataSource, "getURL");
161+
DatabaseDriver vendor = DatabaseDriver.fromJdbcUrl(url);
162+
if (vendor != DatabaseDriver.UNKNOWN) {
163+
for (int i = 0; i < locations.length; i++) {
164+
locations[i] = locations[i].replace(VENDOR_PLACEHOLDER,
165+
vendor.getId());
166+
}
167+
}
168+
}
169+
catch (MetaDataAccessException ex) {
170+
throw new IllegalStateException(ex);
171+
}
172+
}
173+
return locations;
174+
}
175+
176+
private boolean usesVendorLocation(String... locations) {
177+
for (String location : locations) {
178+
if (location.contains(VENDOR_PLACEHOLDER)) {
179+
return true;
180+
}
181+
}
182+
return false;
183+
}
184+
185+
private void checkLocationExists(String... locations) {
186+
if (this.properties.isCheckLocation()) {
187+
Assert.state(locations.length != 0,
188+
"Migration script locations not configured");
189+
boolean exists = hasAtLeastOneLocation(locations);
190+
Assert.state(exists,
191+
() -> "Cannot find migrations location in: " + Arrays.asList(locations)
192+
+ " (please add migrations or check your Flyway configuration)");
193+
}
194+
}
195+
196+
private boolean hasAtLeastOneLocation(String... locations) {
197+
for (String location : locations) {
198+
if (this.resourceLoader.getResource(location).exists()) {
199+
return true;
200+
}
201+
}
202+
return false;
203+
}
204+
163205
@Bean
164206
@ConditionalOnMissingBean
165207
public FlywayMigrationInitializer flywayInitializer(Flyway flyway) {
@@ -200,42 +242,6 @@ public FlywayJpaDependencyConfiguration() {
200242

201243
}
202244

203-
private static class SpringBootFlyway extends Flyway {
204-
205-
private static final String VENDOR_PLACEHOLDER = "{vendor}";
206-
207-
@Override
208-
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-
}
225-
super.setLocations(locations);
226-
}
227-
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-
237-
}
238-
239245
/**
240246
* Convert a String or Number to a {@link MigrationVersion}.
241247
*/

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)