From baf1dd3575761e991c6742dacdf08cdc6e7a577e Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Fri, 5 Jun 2020 15:16:39 +0300 Subject: [PATCH 1/3] Take Java migration classes into account in Flyway extension Fixes: #9819 --- .../io/quarkus/flyway/FlywayProcessor.java | 32 ++++++++++ ...ndMigrateAtStartWithJavaMigrationTest.java | 62 +++++++++++++++++++ .../flyway/runtime/FlywayRecorder.java | 5 ++ .../runtime/QuarkusPathLocationScanner.java | 9 ++- 4 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionCleanAndMigrateAtStartWithJavaMigrationTest.java diff --git a/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/FlywayProcessor.java b/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/FlywayProcessor.java index 66a55323b91ba..c69a2916780f1 100644 --- a/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/FlywayProcessor.java +++ b/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/FlywayProcessor.java @@ -3,6 +3,7 @@ import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; import java.io.IOException; +import java.lang.reflect.Modifier; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -27,6 +28,10 @@ import javax.enterprise.inject.Default; import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.migration.BaseJavaMigration; +import org.flywaydb.core.api.migration.JavaMigration; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; import org.jboss.logging.Logger; import io.quarkus.agroal.deployment.JdbcDataSourceBuildItem; @@ -42,9 +47,12 @@ import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; import io.quarkus.deployment.builditem.CapabilityBuildItem; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.ServiceStartBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.deployment.recording.RecorderContext; import io.quarkus.flyway.runtime.FlywayBuildTimeConfig; import io.quarkus.flyway.runtime.FlywayContainerProducer; import io.quarkus.flyway.runtime.FlywayRecorder; @@ -57,6 +65,9 @@ class FlywayProcessor { private static final String FLYWAY_BEAN_NAME_PREFIX = "flyway_"; + private static final DotName JAVA_MIGRATION = DotName.createSimple(JavaMigration.class.getName()); + private static final DotName BASE_JAVA_MIGRATION = DotName.createSimple(BaseJavaMigration.class.getName()); + private static final Logger LOGGER = Logger.getLogger(FlywayProcessor.class); FlywayBuildTimeConfig flywayBuildConfig; @@ -77,7 +88,10 @@ void scannerTransformer(BuildProducer transformers @BuildStep void build(BuildProducer featureProducer, BuildProducer resourceProducer, + BuildProducer reflectiveClassProducer, FlywayRecorder recorder, + RecorderContext context, + CombinedIndexBuildItem combinedIndexBuildItem, List jdbcDataSourceBuildItems) throws IOException, URISyntaxException { featureProducer.produce(new FeatureBuildItem(FeatureBuildItem.FLYWAY)); @@ -87,9 +101,27 @@ void build(BuildProducer featureProducer, List applicationMigrations = discoverApplicationMigrations(getMigrationLocations(dataSourceNames)); recorder.setApplicationMigrationFiles(applicationMigrations); + Set> javaMigrationClasses = new HashSet<>(); + addJavaMigrations(combinedIndexBuildItem.getIndex().getAllKnownImplementors(JAVA_MIGRATION), context, + reflectiveClassProducer, javaMigrationClasses); + addJavaMigrations(combinedIndexBuildItem.getIndex().getAllKnownSubclasses(BASE_JAVA_MIGRATION), context, + reflectiveClassProducer, javaMigrationClasses); + recorder.setApplicationMigrationClasses(new ArrayList<>(javaMigrationClasses)); + resourceProducer.produce(new NativeImageResourceBuildItem(applicationMigrations.toArray(new String[0]))); } + private void addJavaMigrations(Collection candidates, RecorderContext context, + BuildProducer reflectiveClassProducer, Set> javaMigrationClasses) { + for (ClassInfo javaMigration : candidates) { + if (Modifier.isAbstract(javaMigration.flags())) { + continue; + } + javaMigrationClasses.add(context.classProxy(javaMigration.name().toString())); + reflectiveClassProducer.produce(new ReflectiveClassBuildItem(false, false, javaMigration.name().toString())); + } + } + @BuildStep @Record(ExecutionTime.RUNTIME_INIT) ServiceStartBuildItem createBeansAndStartActions(FlywayRecorder recorder, diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionCleanAndMigrateAtStartWithJavaMigrationTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionCleanAndMigrateAtStartWithJavaMigrationTest.java new file mode 100644 index 0000000000000..b6e4bcdcfe883 --- /dev/null +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionCleanAndMigrateAtStartWithJavaMigrationTest.java @@ -0,0 +1,62 @@ +package io.quarkus.flyway.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import javax.inject.Inject; + +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.migration.BaseJavaMigration; +import org.flywaydb.core.api.migration.Context; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.agroal.api.AgroalDataSource; +import io.quarkus.test.QuarkusUnitTest; + +public class FlywayExtensionCleanAndMigrateAtStartWithJavaMigrationTest { + + @Inject + Flyway flyway; + + @Inject + AgroalDataSource defaultDataSource; + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClass(V1_0_1__Update.class) + .addAsResource("db/migration/V1.0.0__Quarkus.sql") + .addAsResource("clean-and-migrate-at-start-config.properties", "application.properties")); + + @Test + @DisplayName("Clean and migrate at start correctly") + public void testFlywayConfigInjection() throws SQLException { + + try (Connection connection = defaultDataSource.getConnection(); Statement stat = connection.createStatement()) { + try (ResultSet countQuery = stat.executeQuery("select count(1) from quarked_flyway")) { + countQuery.first(); + int count = countQuery.getInt(1); + assertEquals(1, count, "Table 'quarked_flyway' does not contain the expected number of rows"); + } + } + String currentVersion = flyway.info().current().getVersion().toString(); + assertEquals("1.0.1", currentVersion, "Expected to be 1.0.1 as there is both a SQL and a Java migration script"); + } + + public static class V1_0_1__Update extends BaseJavaMigration { + @Override + public void migrate(Context context) throws Exception { + try (Statement statement = context.getConnection().createStatement()) { + statement.executeUpdate("INSERT INTO quarked_flyway VALUES (1001, 'test')"); + } + } + } +} diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayRecorder.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayRecorder.java index b0e2aa506c650..ace0fdbf32ea4 100644 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayRecorder.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayRecorder.java @@ -25,6 +25,11 @@ public void setApplicationMigrationFiles(List migrationFiles) { QuarkusPathLocationScanner.setApplicationMigrationFiles(migrationFiles); } + public void setApplicationMigrationClasses(List> migrationClasses) { + log.debugv("Setting the following application migration classes: {0}", migrationClasses); + QuarkusPathLocationScanner.setApplicationMigrationClasses(migrationClasses); + } + public Supplier flywaySupplier(String dataSourceName) { DataSource dataSource = DataSources.fromName(dataSourceName); FlywayContainerProducer flywayProducer = Arc.container().instance(FlywayContainerProducer.class).get(); diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/QuarkusPathLocationScanner.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/QuarkusPathLocationScanner.java index dcae67c55a8c6..09153f5bb0f38 100644 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/QuarkusPathLocationScanner.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/QuarkusPathLocationScanner.java @@ -3,7 +3,6 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import org.flywaydb.core.api.Location; @@ -21,6 +20,7 @@ public final class QuarkusPathLocationScanner implements ResourceAndClassScanner private static final Logger LOGGER = Logger.getLogger(QuarkusPathLocationScanner.class); private static final String LOCATION_SEPARATOR = "/"; private static List applicationMigrationFiles; + private static List> applicationMigrationClasses; private final Collection scannedResources; @@ -74,11 +74,14 @@ private boolean canHandleMigrationFile(Collection locations, String mi */ @Override public Collection> scanForClasses() { - // Classes are not supported in native mode - return Collections.emptyList(); + return applicationMigrationClasses; } public static void setApplicationMigrationFiles(List applicationMigrationFiles) { QuarkusPathLocationScanner.applicationMigrationFiles = applicationMigrationFiles; } + + public static void setApplicationMigrationClasses(List> applicationMigrationClasses) { + QuarkusPathLocationScanner.applicationMigrationClasses = applicationMigrationClasses; + } } From 2a463d1590f9c36f5abb517fa78a583e4610223e Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Fri, 5 Jun 2020 12:26:25 -0300 Subject: [PATCH 2/3] Index flyway dependency to look for all JavaMigration implementations --- .../io/quarkus/flyway/FlywayProcessor.java | 10 ++-- ...ndMigrateAtStartWithJavaMigrationTest.java | 49 +++++++++++++++++-- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/FlywayProcessor.java b/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/FlywayProcessor.java index c69a2916780f1..2ed4effc0fab9 100644 --- a/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/FlywayProcessor.java +++ b/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/FlywayProcessor.java @@ -28,7 +28,6 @@ import javax.enterprise.inject.Default; import org.flywaydb.core.Flyway; -import org.flywaydb.core.api.migration.BaseJavaMigration; import org.flywaydb.core.api.migration.JavaMigration; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; @@ -49,6 +48,7 @@ import io.quarkus.deployment.builditem.CapabilityBuildItem; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.IndexDependencyBuildItem; import io.quarkus.deployment.builditem.ServiceStartBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; @@ -66,7 +66,6 @@ class FlywayProcessor { private static final String FLYWAY_BEAN_NAME_PREFIX = "flyway_"; private static final DotName JAVA_MIGRATION = DotName.createSimple(JavaMigration.class.getName()); - private static final DotName BASE_JAVA_MIGRATION = DotName.createSimple(BaseJavaMigration.class.getName()); private static final Logger LOGGER = Logger.getLogger(FlywayProcessor.class); @@ -84,6 +83,11 @@ void scannerTransformer(BuildProducer transformers new ScannerTransformer())); } + @BuildStep + IndexDependencyBuildItem indexFlyway() { + return new IndexDependencyBuildItem("org.flywaydb", "flyway-core"); + } + @Record(STATIC_INIT) @BuildStep void build(BuildProducer featureProducer, @@ -104,8 +108,6 @@ void build(BuildProducer featureProducer, Set> javaMigrationClasses = new HashSet<>(); addJavaMigrations(combinedIndexBuildItem.getIndex().getAllKnownImplementors(JAVA_MIGRATION), context, reflectiveClassProducer, javaMigrationClasses); - addJavaMigrations(combinedIndexBuildItem.getIndex().getAllKnownSubclasses(BASE_JAVA_MIGRATION), context, - reflectiveClassProducer, javaMigrationClasses); recorder.setApplicationMigrationClasses(new ArrayList<>(javaMigrationClasses)); resourceProducer.produce(new NativeImageResourceBuildItem(applicationMigrations.toArray(new String[0]))); diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionCleanAndMigrateAtStartWithJavaMigrationTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionCleanAndMigrateAtStartWithJavaMigrationTest.java index b6e4bcdcfe883..4be77cd70c713 100644 --- a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionCleanAndMigrateAtStartWithJavaMigrationTest.java +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionCleanAndMigrateAtStartWithJavaMigrationTest.java @@ -1,6 +1,7 @@ package io.quarkus.flyway.test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.ResultSet; @@ -10,8 +11,10 @@ import javax.inject.Inject; import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.MigrationVersion; import org.flywaydb.core.api.migration.BaseJavaMigration; import org.flywaydb.core.api.migration.Context; +import org.flywaydb.core.api.migration.JavaMigration; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.api.DisplayName; @@ -32,7 +35,7 @@ public class FlywayExtensionCleanAndMigrateAtStartWithJavaMigrationTest { @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClass(V1_0_1__Update.class) + .addClasses(V1_0_1__Update.class, V1_0_2__Update.class) .addAsResource("db/migration/V1.0.0__Quarkus.sql") .addAsResource("clean-and-migrate-at-start-config.properties", "application.properties")); @@ -42,13 +45,14 @@ public void testFlywayConfigInjection() throws SQLException { try (Connection connection = defaultDataSource.getConnection(); Statement stat = connection.createStatement()) { try (ResultSet countQuery = stat.executeQuery("select count(1) from quarked_flyway")) { - countQuery.first(); - int count = countQuery.getInt(1); - assertEquals(1, count, "Table 'quarked_flyway' does not contain the expected number of rows"); + assertTrue(countQuery.first()); + assertEquals(2, + countQuery.getInt(1), + "Table 'quarked_flyway' does not contain the expected number of rows"); } } String currentVersion = flyway.info().current().getVersion().toString(); - assertEquals("1.0.1", currentVersion, "Expected to be 1.0.1 as there is both a SQL and a Java migration script"); + assertEquals("1.0.2", currentVersion, "Expected to be 1.0.2 as there is a SQL and two Java migration scripts"); } public static class V1_0_1__Update extends BaseJavaMigration { @@ -59,4 +63,39 @@ public void migrate(Context context) throws Exception { } } } + + public static class V1_0_2__Update implements JavaMigration { + @Override + public MigrationVersion getVersion() { + return MigrationVersion.fromVersion("1.0.2"); + } + + @Override + public String getDescription() { + return getClass().getSimpleName(); + } + + @Override + public Integer getChecksum() { + return null; + } + + @Override + public boolean isUndo() { + return false; + } + + @Override + public boolean canExecuteInTransaction() { + return true; + } + + @Override + public void migrate(Context context) throws Exception { + try (Statement statement = context.getConnection().createStatement()) { + statement.executeUpdate("INSERT INTO quarked_flyway VALUES (1002, 'test')"); + } + } + } + } From 7a5efed2a97d88656484b431b472210e2bb7d2f3 Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Fri, 5 Jun 2020 13:23:26 -0300 Subject: [PATCH 3/3] Use Collection instead of forcing to List --- .../src/main/java/io/quarkus/flyway/FlywayProcessor.java | 2 +- .../java/io/quarkus/flyway/runtime/FlywayRecorder.java | 5 +++-- .../flyway/runtime/QuarkusPathLocationScanner.java | 9 ++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/FlywayProcessor.java b/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/FlywayProcessor.java index 2ed4effc0fab9..93c080cf454f0 100644 --- a/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/FlywayProcessor.java +++ b/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/FlywayProcessor.java @@ -108,7 +108,7 @@ void build(BuildProducer featureProducer, Set> javaMigrationClasses = new HashSet<>(); addJavaMigrations(combinedIndexBuildItem.getIndex().getAllKnownImplementors(JAVA_MIGRATION), context, reflectiveClassProducer, javaMigrationClasses); - recorder.setApplicationMigrationClasses(new ArrayList<>(javaMigrationClasses)); + recorder.setApplicationMigrationClasses(javaMigrationClasses); resourceProducer.produce(new NativeImageResourceBuildItem(applicationMigrations.toArray(new String[0]))); } diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayRecorder.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayRecorder.java index ace0fdbf32ea4..1fd43e768c5c4 100644 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayRecorder.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayRecorder.java @@ -1,6 +1,7 @@ package io.quarkus.flyway.runtime; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.function.Supplier; @@ -20,12 +21,12 @@ public class FlywayRecorder { private final List flywayContainers = new ArrayList<>(2); - public void setApplicationMigrationFiles(List migrationFiles) { + public void setApplicationMigrationFiles(Collection migrationFiles) { log.debugv("Setting the following application migration files: {0}", migrationFiles); QuarkusPathLocationScanner.setApplicationMigrationFiles(migrationFiles); } - public void setApplicationMigrationClasses(List> migrationClasses) { + public void setApplicationMigrationClasses(Collection> migrationClasses) { log.debugv("Setting the following application migration classes: {0}", migrationClasses); QuarkusPathLocationScanner.setApplicationMigrationClasses(migrationClasses); } diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/QuarkusPathLocationScanner.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/QuarkusPathLocationScanner.java index 09153f5bb0f38..00be7c6752739 100644 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/QuarkusPathLocationScanner.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/QuarkusPathLocationScanner.java @@ -3,7 +3,6 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; -import java.util.List; import org.flywaydb.core.api.Location; import org.flywaydb.core.internal.resource.LoadableResource; @@ -19,8 +18,8 @@ public final class QuarkusPathLocationScanner implements ResourceAndClassScanner { private static final Logger LOGGER = Logger.getLogger(QuarkusPathLocationScanner.class); private static final String LOCATION_SEPARATOR = "/"; - private static List applicationMigrationFiles; - private static List> applicationMigrationClasses; + private static Collection applicationMigrationFiles; + private static Collection> applicationMigrationClasses; private final Collection scannedResources; @@ -77,11 +76,11 @@ public Collection> scanForClasses() { return applicationMigrationClasses; } - public static void setApplicationMigrationFiles(List applicationMigrationFiles) { + public static void setApplicationMigrationFiles(Collection applicationMigrationFiles) { QuarkusPathLocationScanner.applicationMigrationFiles = applicationMigrationFiles; } - public static void setApplicationMigrationClasses(List> applicationMigrationClasses) { + public static void setApplicationMigrationClasses(Collection> applicationMigrationClasses) { QuarkusPathLocationScanner.applicationMigrationClasses = applicationMigrationClasses; } }