Skip to content

Commit 5ef5685

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 2bf4287 commit 5ef5685

File tree

4 files changed

+132
-30
lines changed

4 files changed

+132
-30
lines changed

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

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.HashSet;
2121
import java.util.Set;
2222

23-
import javax.annotation.PostConstruct;
2423
import javax.persistence.EntityManagerFactory;
2524
import javax.sql.DataSource;
2625

@@ -45,12 +44,10 @@
4544
import org.springframework.context.annotation.Configuration;
4645
import org.springframework.core.convert.TypeDescriptor;
4746
import org.springframework.core.convert.converter.GenericConverter;
48-
import org.springframework.core.io.ResourceLoader;
4947
import org.springframework.jdbc.support.JdbcUtils;
5048
import org.springframework.jdbc.support.MetaDataAccessException;
5149
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
5250
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
53-
import org.springframework.util.Assert;
5451
import org.springframework.util.ObjectUtils;
5552

5653
/**
@@ -84,47 +81,22 @@ public static class FlywayConfiguration {
8481

8582
private final FlywayProperties properties;
8683

87-
private final ResourceLoader resourceLoader;
88-
8984
private final DataSource dataSource;
9085

9186
private final DataSource flywayDataSource;
9287

9388
private final FlywayMigrationStrategy migrationStrategy;
9489

9590
public FlywayConfiguration(FlywayProperties properties,
96-
ResourceLoader resourceLoader, ObjectProvider<DataSource> dataSource,
91+
ObjectProvider<DataSource> dataSource,
9792
@FlywayDataSource ObjectProvider<DataSource> flywayDataSource,
9893
ObjectProvider<FlywayMigrationStrategy> migrationStrategy) {
9994
this.properties = properties;
100-
this.resourceLoader = resourceLoader;
10195
this.dataSource = dataSource.getIfUnique();
10296
this.flywayDataSource = flywayDataSource.getIfAvailable();
10397
this.migrationStrategy = migrationStrategy.getIfAvailable();
10498
}
10599

106-
@PostConstruct
107-
public void checkLocationExists() {
108-
if (this.properties.isCheckLocation()) {
109-
Assert.state(!this.properties.getLocations().isEmpty(),
110-
"Migration script locations not configured");
111-
boolean exists = hasAtLeastOneLocation();
112-
Assert.state(exists,
113-
"Cannot find migrations location in: " + this.properties
114-
.getLocations()
115-
+ " (please add migrations or check your Flyway configuration)");
116-
}
117-
}
118-
119-
private boolean hasAtLeastOneLocation() {
120-
for (String location : this.properties.getLocations()) {
121-
if (this.resourceLoader.getResource(location).exists()) {
122-
return true;
123-
}
124-
}
125-
return false;
126-
}
127-
128100
@Bean
129101
@ConfigurationProperties(prefix = "flyway")
130102
public Flyway flyway() {
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.flyway;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
import javax.annotation.PostConstruct;
23+
import javax.sql.DataSource;
24+
25+
import org.flywaydb.core.Flyway;
26+
27+
import org.springframework.beans.factory.ObjectProvider;
28+
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
29+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
30+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
31+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
32+
import org.springframework.boot.jdbc.DatabaseDriver;
33+
import org.springframework.context.annotation.Configuration;
34+
import org.springframework.core.io.ResourceLoader;
35+
import org.springframework.jdbc.support.JdbcUtils;
36+
import org.springframework.jdbc.support.MetaDataAccessException;
37+
import org.springframework.util.Assert;
38+
39+
/**
40+
* {@link EnableAutoConfiguration Auto configuration} to validate flyway locations on
41+
* startup.
42+
*
43+
* @author Eddú Meléndez
44+
*/
45+
@Configuration
46+
@AutoConfigureBefore(FlywayAutoConfiguration.class)
47+
@ConditionalOnProperty(prefix = "flyway", name = "check-location", havingValue = "true")
48+
@EnableConfigurationProperties(FlywayProperties.class)
49+
public class FlywayValidatorAutoConfiguration {
50+
51+
private static final String VENDOR_PLACEHOLDER = "{vendor}";
52+
53+
private final FlywayProperties properties;
54+
55+
private final ResourceLoader resourceLoader;
56+
57+
private final DataSource dataSource;
58+
59+
private final DataSource flywayDataSource;
60+
61+
public FlywayValidatorAutoConfiguration(FlywayProperties properties,
62+
ResourceLoader resourceLoader, ObjectProvider<DataSource> dataSource,
63+
@FlywayDataSource ObjectProvider<DataSource> flywayDataSource) {
64+
this.properties = properties;
65+
this.resourceLoader = resourceLoader;
66+
this.dataSource = dataSource.getIfAvailable();
67+
this.flywayDataSource = flywayDataSource.getIfAvailable();
68+
}
69+
70+
@PostConstruct
71+
public void checkLocationExists() {
72+
Flyway flyway = new Flyway();
73+
if (this.properties.isCreateDataSource()) {
74+
flyway.setDataSource(this.properties.getUrl(), this.properties.getUser(),
75+
this.properties.getPassword(),
76+
this.properties.getInitSqls().toArray(new String[0]));
77+
}
78+
else if (this.flywayDataSource != null) {
79+
flyway.setDataSource(this.flywayDataSource);
80+
}
81+
else {
82+
flyway.setDataSource(this.dataSource);
83+
}
84+
85+
List<String> locations = new ArrayList<String>();
86+
try {
87+
String url = (String) JdbcUtils.extractDatabaseMetaData(
88+
flyway.getDataSource(), "getURL");
89+
DatabaseDriver vendor = DatabaseDriver.fromJdbcUrl(url);
90+
if (vendor != DatabaseDriver.UNKNOWN) {
91+
for (int i = 0; i < this.properties.getLocations().size(); i++) {
92+
locations.add(this.properties.getLocations().get(i)
93+
.replace(VENDOR_PLACEHOLDER, vendor.getId()));
94+
}
95+
}
96+
}
97+
catch (MetaDataAccessException ex) {
98+
throw new IllegalStateException(ex);
99+
}
100+
101+
Assert.state(!(locations.isEmpty()), "Migration script locations not configured");
102+
boolean exists = hasAtLeastOneLocation(locations);
103+
Assert.state(exists, "Cannot find migrations location in: " + locations
104+
+ " (please add migrations or check your Flyway configuration)");
105+
}
106+
107+
private boolean hasAtLeastOneLocation(List<String> locations) {
108+
for (String location : locations) {
109+
if (this.resourceLoader.getResource(location).exists()) {
110+
return true;
111+
}
112+
}
113+
return false;
114+
}
115+
116+
}

spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguratio
6969
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
7070
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
7171
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
72+
org.springframework.boot.autoconfigure.flyway.FlywayValidatorAutoConfiguration,\
7273
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
7374
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
7475
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
* @author Phillip Webb
5858
* @author Andy Wilkinson
5959
* @author Vedran Pavic
60+
* @author Eddú Meléndez
6061
*/
6162
public class FlywayAutoConfigurationTests {
6263

@@ -168,7 +169,7 @@ public void checkLocationsAllMissing() throws Exception {
168169
this.thrown.expect(BeanCreationException.class);
169170
this.thrown.expectMessage("Cannot find migrations location in");
170171
registerAndRefresh(EmbeddedDataSourceConfiguration.class,
171-
FlywayAutoConfiguration.class,
172+
FlywayAutoConfiguration.class, FlywayValidatorAutoConfiguration.class,
172173
PropertyPlaceholderAutoConfiguration.class);
173174
}
174175

@@ -246,6 +247,18 @@ public void useVendorDirectory() throws Exception {
246247
"classpath:db/vendors/h2", "classpath:db/changelog");
247248
}
248249

250+
@Test
251+
public void useVendorDirectoryAndCheckLocation() throws Exception {
252+
EnvironmentTestUtils.addEnvironment(this.context,
253+
"flyway.locations=classpath:db/vendors/{vendor}",
254+
"flyway.check-location=true");
255+
registerAndRefresh(EmbeddedDataSourceConfiguration.class,
256+
FlywayAutoConfiguration.class, FlywayValidatorAutoConfiguration.class,
257+
PropertyPlaceholderAutoConfiguration.class);
258+
Flyway flyway = this.context.getBean(Flyway.class);
259+
assertThat(flyway.getLocations()).containsExactly("classpath:db/vendors/h2");
260+
}
261+
249262
private void registerAndRefresh(Class<?>... annotatedClasses) {
250263
this.context.register(annotatedClasses);
251264
this.context.refresh();

0 commit comments

Comments
 (0)