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 a7e93b42157..af714f241d2 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,9 +36,11 @@ import org.flywaydb.core.api.configuration.FluentConfiguration; import org.flywaydb.core.api.migration.JavaMigration; import org.flywaydb.core.extensibility.ConfigurationExtension; import org.flywaydb.core.internal.database.postgresql.PostgreSQLConfigurationExtension; +import org.flywaydb.core.internal.scanner.Scanner; import org.flywaydb.database.oracle.OracleConfigurationExtension; import org.flywaydb.database.sqlserver.SQLServerConfigurationExtension; +import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.beans.factory.ObjectProvider; @@ -78,6 +80,7 @@ import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.jdbc.support.JdbcUtils; import org.springframework.jdbc.support.MetaDataAccessException; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -461,6 +464,9 @@ public class FlywayAutoConfiguration { @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { hints.resources().registerPattern("db/migration/*"); + if (ClassUtils.isPresent("org.flywaydb.core.extensibility.Tier", classLoader)) { + hints.reflection().registerType(Scanner.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS); + } } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProviderCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProviderCustomizer.java index 1dd7b6e55ec..3abbee4bf94 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProviderCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/NativeImageResourceProviderCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,10 @@ package org.springframework.boot.autoconfigure.flyway; +import java.lang.reflect.Constructor; import java.util.Arrays; +import org.flywaydb.core.api.configuration.Configuration; import org.flywaydb.core.api.configuration.FluentConfiguration; import org.flywaydb.core.api.migration.JavaMigration; import org.flywaydb.core.internal.scanner.LocationScannerCache; @@ -29,16 +31,14 @@ import org.flywaydb.core.internal.scanner.Scanner; * {@link org.flywaydb.core.api.ResourceProvider}. * * @author Moritz Halbritter + * @author Maziz Esa */ class NativeImageResourceProviderCustomizer extends ResourceProviderCustomizer { @Override public void customize(FluentConfiguration configuration) { if (configuration.getResourceProvider() == null) { - Scanner scanner = new Scanner<>(JavaMigration.class, - Arrays.asList(configuration.getLocations()), configuration.getClassLoader(), - configuration.getEncoding(), configuration.isDetectEncoding(), false, new ResourceNameCache(), - new LocationScannerCache(), configuration.isFailOnMissingLocations()); + Scanner scanner = createScanner(configuration); NativeImageResourceProvider resourceProvider = new NativeImageResourceProvider(scanner, configuration.getClassLoader(), Arrays.asList(configuration.getLocations()), configuration.getEncoding(), configuration.isFailOnMissingLocations()); @@ -46,4 +46,29 @@ class NativeImageResourceProviderCustomizer extends ResourceProviderCustomizer { } } + private static Scanner createScanner(FluentConfiguration configuration) { + try { + return new Scanner<>(JavaMigration.class, Arrays.asList(configuration.getLocations()), + configuration.getClassLoader(), configuration.getEncoding(), configuration.isDetectEncoding(), + false, new ResourceNameCache(), new LocationScannerCache(), + configuration.isFailOnMissingLocations()); + } + catch (NoSuchMethodError ex) { + // Flyway 10 + return createFlyway10Scanner(configuration); + } + } + + private static Scanner createFlyway10Scanner(FluentConfiguration configuration) throws LinkageError { + try { + Constructor scannerConstructor = Scanner.class.getDeclaredConstructor(Class.class, boolean.class, + ResourceNameCache.class, LocationScannerCache.class, Configuration.class); + return (Scanner) scannerConstructor.newInstance(JavaMigration.class, false, new ResourceNameCache(), + new LocationScannerCache(), configuration); + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/Flyway10xNativeImageResourceProviderCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/Flyway10xNativeImageResourceProviderCustomizerTests.java new file mode 100644 index 00000000000..29d60254b65 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/Flyway10xNativeImageResourceProviderCustomizerTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.flyway; + +import java.util.Collection; + +import org.flywaydb.core.api.ResourceProvider; +import org.flywaydb.core.api.configuration.FluentConfiguration; +import org.flywaydb.core.api.resource.LoadableResource; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.testsupport.classpath.ClassPathOverrides; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link NativeImageResourceProviderCustomizer} with Flyway 10. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Maziz Esa + */ +@ClassPathOverrides("org.flywaydb:flyway-core:10.12.0") +class Flyway10xNativeImageResourceProviderCustomizerTests { + + private final NativeImageResourceProviderCustomizer customizer = new NativeImageResourceProviderCustomizer(); + + @Test + void nativeImageResourceProviderShouldFindMigrations() { + FluentConfiguration configuration = new FluentConfiguration(); + this.customizer.customize(configuration); + ResourceProvider resourceProvider = configuration.getResourceProvider(); + Collection migrations = resourceProvider.getResources("V", new String[] { ".sql" }); + LoadableResource migration = resourceProvider.getResource("V1__init.sql"); + assertThat(migrations).containsExactly(migration); + } + +}