From 345edb13013095f104cbdf2fee4c6bc3e7dd4933 Mon Sep 17 00:00:00 2001 From: Maziz Date: Sun, 19 May 2024 13:05:55 +0800 Subject: [PATCH] Fix Flyway 10 in a GraalVM native image See gh-40821 --- ...NativeImageResourceProviderCustomizer.java | 47 +++++++++++++++-- ...eImageResourceProviderCustomizerTests.java | 52 +++++++++++++++++++ 2 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/Flyway10NativeImageResourceProviderCustomizerTests.java 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..4e7668379eb 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 @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.flyway; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import org.flywaydb.core.api.configuration.FluentConfiguration; @@ -24,21 +26,21 @@ import org.flywaydb.core.internal.scanner.LocationScannerCache; import org.flywaydb.core.internal.scanner.ResourceNameCache; import org.flywaydb.core.internal.scanner.Scanner; +import org.springframework.util.ClassUtils; + /** * Registers {@link NativeImageResourceProvider} as a Flyway * {@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()); + final var scanner = getFlyway9OrFallbackTo10ScannerObject(configuration); NativeImageResourceProvider resourceProvider = new NativeImageResourceProvider(scanner, configuration.getClassLoader(), Arrays.asList(configuration.getLocations()), configuration.getEncoding(), configuration.isFailOnMissingLocations()); @@ -46,4 +48,41 @@ class NativeImageResourceProviderCustomizer extends ResourceProviderCustomizer { } } + private static Scanner getFlyway9OrFallbackTo10ScannerObject(FluentConfiguration configuration) { + Scanner scanner; + try { + scanner = getFlyway9Scanner(configuration); + } + catch (NoSuchMethodError noSuchMethodError) { + // happens when scanner is flyway version 10, which the constructor accepts + // different number of parameters. + scanner = getFlyway10Scanner(configuration); + } + return scanner; + } + + private static Scanner getFlyway10Scanner(FluentConfiguration configuration) { + final Constructor scannerConstructor; + final Scanner scanner; + try { + scannerConstructor = ClassUtils.forName("org.flywaydb.core.internal.scanner.Scanner", null) + .getDeclaredConstructors()[0]; + scanner = (Scanner) scannerConstructor.newInstance(JavaMigration.class, false, new ResourceNameCache(), + new LocationScannerCache(), configuration); + } + catch (ClassNotFoundException | InstantiationException | IllegalAccessException + | InvocationTargetException ex) { + throw new RuntimeException(ex); + } + return scanner; + } + + private static Scanner getFlyway9Scanner(FluentConfiguration configuration) { + Scanner scanner; + scanner = new Scanner<>(JavaMigration.class, Arrays.asList(configuration.getLocations()), + configuration.getClassLoader(), configuration.getEncoding(), configuration.isDetectEncoding(), false, + new ResourceNameCache(), new LocationScannerCache(), configuration.isFailOnMissingLocations()); + return scanner; + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/Flyway10NativeImageResourceProviderCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/Flyway10NativeImageResourceProviderCustomizerTests.java new file mode 100644 index 00000000000..9e3e5e4db3b --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/Flyway10NativeImageResourceProviderCustomizerTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2023 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 + */ +@ClassPathOverrides("org.flywaydb:flyway-core:10.12.0") +class Flyway10NativeImageResourceProviderCustomizerTests { + + 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); + } + +}