Skip to content

Commit

Permalink
create flyway build item
Browse files Browse the repository at this point in the history
  • Loading branch information
rmanibus committed Oct 18, 2024
1 parent 5e4a69e commit 0838b2c
Show file tree
Hide file tree
Showing 12 changed files with 363 additions and 245 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package io.quarkus.flyway.deployment;

import org.jboss.jandex.AnnotationInstance;

import io.quarkus.builder.item.MultiBuildItem;
import io.quarkus.flyway.runtime.FlywayContainerProducer;
import io.quarkus.flyway.runtime.FlywayDataSourceBuildTimeConfig;

public final class FlywayBuildItem extends MultiBuildItem {

private final Class<? extends FlywayContainerProducer> producerClass;
private final FlywayDataSourceBuildTimeConfig buildTimeConfig;
private final String name;
private final String dataSourceName;
private final AnnotationInstance qualifier;
private final int priority;
private final String containerBeanName;
private final String flywayBeanName;

public FlywayBuildItem(Class<? extends FlywayContainerProducer> producerClass,
FlywayDataSourceBuildTimeConfig buildTimeConfig, String name, String dataSourceName, AnnotationInstance qualifier,
int priority, String containerBeanName, String flywayBeanName) {
this.producerClass = producerClass;
this.buildTimeConfig = buildTimeConfig;
this.name = name;
this.dataSourceName = dataSourceName;
this.qualifier = qualifier;
this.priority = priority;
this.containerBeanName = containerBeanName;
this.flywayBeanName = flywayBeanName;
}

public Class<? extends FlywayContainerProducer> getProducerClass() {
return producerClass;
}

public String getName() {
return name;
}

public String getId() {
return producerClass + name;
}

public String getDataSourceName() {
return dataSourceName;
}

public AnnotationInstance getQualifier() {
return qualifier;
}

public int getPriority() {
return priority;
}

public String getContainerBeanName() {
return containerBeanName;
}

public String getFlywayBeanName() {
return flywayBeanName;
}

public FlywayDataSourceBuildTimeConfig getBuildTimeConfig() {
return buildTimeConfig;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,20 @@
* This class also helps to keep the {@link FlywayProcessor} class as lean as possible to make it easier to maintain
*/
class FlywayCallbacksLocator {
private final Collection<String> dataSourceNames;
private final FlywayBuildTimeConfig flywayBuildConfig;
private final Collection<FlywayBuildItem> flywayBuildItems;
private final CombinedIndexBuildItem combinedIndexBuildItem;
private final BuildProducer<ReflectiveClassBuildItem> reflectiveClassProducer;

private FlywayCallbacksLocator(Collection<String> dataSourceNames, FlywayBuildTimeConfig flywayBuildConfig,
private FlywayCallbacksLocator(Collection<FlywayBuildItem> flywayBuildItems,
CombinedIndexBuildItem combinedIndexBuildItem, BuildProducer<ReflectiveClassBuildItem> reflectiveClassProducer) {
this.dataSourceNames = dataSourceNames;
this.flywayBuildConfig = flywayBuildConfig;
this.flywayBuildItems = flywayBuildItems;
this.combinedIndexBuildItem = combinedIndexBuildItem;
this.reflectiveClassProducer = reflectiveClassProducer;
}

public static FlywayCallbacksLocator with(Collection<String> dataSourceNames, FlywayBuildTimeConfig flywayBuildConfig,
public static FlywayCallbacksLocator with(Collection<FlywayBuildItem> flywayBuildItems,
CombinedIndexBuildItem combinedIndexBuildItem, BuildProducer<ReflectiveClassBuildItem> reflectiveClassProducer) {
return new FlywayCallbacksLocator(dataSourceNames, flywayBuildConfig, combinedIndexBuildItem, reflectiveClassProducer);
return new FlywayCallbacksLocator(flywayBuildItems, combinedIndexBuildItem, reflectiveClassProducer);
}

/**
Expand All @@ -56,9 +54,9 @@ public static FlywayCallbacksLocator with(Collection<String> dataSourceNames, Fl
public Map<String, Collection<Callback>> getCallbacks()
throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {
final Map<String, Collection<Callback>> callbacks = new HashMap<>();
for (String dataSourceName : dataSourceNames) {
final Collection<Callback> instances = callbacksForDataSource(dataSourceName);
callbacks.put(dataSourceName, instances);
for (FlywayBuildItem flywayBuildItem : flywayBuildItems) {
final Collection<Callback> instances = callbacksFor(flywayBuildItem);
callbacks.put(flywayBuildItem.getName(), instances);
}
return callbacks;
}
Expand All @@ -74,9 +72,9 @@ public Map<String, Collection<Callback>> getCallbacks()
* @exception IllegalAccessException if the {@link Callback} constructor is enforcing Java language access control
* and the underlying constructor is inaccessible
*/
private Collection<Callback> callbacksForDataSource(String dataSourceName)
private Collection<Callback> callbacksFor(FlywayBuildItem flywayBuildItem)
throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
final Optional<List<String>> callbackConfig = flywayBuildConfig.getConfigForDataSourceName(dataSourceName).callbacks;
final Optional<List<String>> callbackConfig = flywayBuildItem.getBuildTimeConfig().callbacks;
if (!callbackConfig.isPresent()) {
return Collections.emptyList();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
import io.quarkus.flyway.FlywayDataSource;
import io.quarkus.flyway.runtime.FlywayBuildTimeConfig;
import io.quarkus.flyway.runtime.FlywayContainer;
import io.quarkus.flyway.runtime.FlywayContainerProducer;
import io.quarkus.flyway.runtime.FlywayContainerProducerImpl;
import io.quarkus.flyway.runtime.FlywayDataSourceBuildTimeConfig;
import io.quarkus.flyway.runtime.FlywayRecorder;
import io.quarkus.flyway.runtime.FlywayRuntimeConfig;
Expand Down Expand Up @@ -107,6 +107,45 @@ void reflection(CombinedIndexBuildItem index, BuildProducer<ReflectiveClassBuild
}
}

@BuildStep
void prepare(BuildProducer<FlywayBuildItem> flywayBuildItems, BuildProducer<AdditionalBeanBuildItem> additionalBeans, List<JdbcDataSourceBuildItem> jdbcDataSourceBuildItems,
FlywayBuildTimeConfig flywayBuildTimeConfig) {

// make a FlywayContainerProducer bean
additionalBeans
.produce(AdditionalBeanBuildItem.builder().addBeanClasses(FlywayContainerProducerImpl.class).setUnremovable()
.setDefaultScope(DotNames.SINGLETON).build());
// add the @FlywayDataSource class otherwise it won't be registered as a qualifier
additionalBeans.produce(AdditionalBeanBuildItem.builder().addBeanClass(FlywayDataSource.class).build());


for (JdbcDataSourceBuildItem item : jdbcDataSourceBuildItems) {
String dataSourceName = item.getName();
FlywayDataSourceBuildTimeConfig buildTimeConfig = flywayBuildTimeConfig
.getConfigForDataSourceName(dataSourceName);
AnnotationInstance qualifier;
int priority;
String flywayBeanName = null;
String containerBeanName = null;

if (DataSourceUtil.isDefault(dataSourceName)) {
// Flyway containers used to be ordered with the default database coming first.
// Some multitenant tests are relying on this order.
priority = 10;
qualifier = AnnotationInstance.builder(Default.class).build();
} else {
priority = 5;
qualifier = AnnotationInstance.builder(FlywayDataSource.class).add("value", dataSourceName)
.build();
flywayBeanName = FLYWAY_BEAN_NAME_PREFIX + dataSourceName;
containerBeanName = FLYWAY_CONTAINER_BEAN_NAME_PREFIX + dataSourceName;
}

flywayBuildItems.produce(new FlywayBuildItem(FlywayContainerProducerImpl.class, buildTimeConfig, dataSourceName,
dataSourceName, qualifier, priority, containerBeanName, flywayBeanName));
}
}

@Record(STATIC_INIT)
@BuildStep
MigrationStateBuildItem build(BuildProducer<NativeImageResourceBuildItem> resourceProducer,
Expand All @@ -115,30 +154,28 @@ MigrationStateBuildItem build(BuildProducer<NativeImageResourceBuildItem> resour
FlywayRecorder recorder,
RecorderContext context,
CombinedIndexBuildItem combinedIndexBuildItem,
List<JdbcDataSourceBuildItem> jdbcDataSourceBuildItems,
List<FlywayBuildItem> flywayBuildItems,
FlywayBuildTimeConfig flywayBuildTimeConfig) throws Exception {

Collection<String> dataSourceNames = getDataSourceNames(jdbcDataSourceBuildItems);
Map<String, Collection<String>> applicationMigrationsToDs = new HashMap<>();
for (var dataSourceName : dataSourceNames) {
FlywayDataSourceBuildTimeConfig flywayDataSourceBuildTimeConfig = flywayBuildTimeConfig
.getConfigForDataSourceName(dataSourceName);
Map<String, Collection<String>> applicationMigrationsToInstance = new HashMap<>();
for (var flywayBuildItem : flywayBuildItems) {
FlywayDataSourceBuildTimeConfig flywayDataSourceBuildTimeConfig = flywayBuildItem.getBuildTimeConfig();

Collection<String> migrationLocations = discoverApplicationMigrations(
flywayDataSourceBuildTimeConfig.locations);
applicationMigrationsToDs.put(dataSourceName, migrationLocations);
applicationMigrationsToInstance.put(flywayBuildItem.getId(), migrationLocations);
}
Set<String> datasourcesWithMigrations = new HashSet<>();
Set<String> datasourcesWithoutMigrations = new HashSet<>();
for (var e : applicationMigrationsToDs.entrySet()) {
Set<String> instancesWithMigrations = new HashSet<>();
Set<String> instancesWithoutMigrations = new HashSet<>();
for (var e : applicationMigrationsToInstance.entrySet()) {
if (e.getValue().isEmpty()) {
datasourcesWithoutMigrations.add(e.getKey());
instancesWithoutMigrations.add(e.getKey());
} else {
datasourcesWithMigrations.add(e.getKey());
instancesWithMigrations.add(e.getKey());
}
}

Collection<String> applicationMigrations = applicationMigrationsToDs.values().stream().collect(HashSet::new,
Collection<String> applicationMigrations = applicationMigrationsToInstance.values().stream().collect(HashSet::new,
AbstractCollection::addAll, HashSet::addAll);
for (String applicationMigration : applicationMigrations) {
Location applicationMigrationLocation = new Location(applicationMigration);
Expand All @@ -158,14 +195,13 @@ MigrationStateBuildItem build(BuildProducer<NativeImageResourceBuildItem> resour
recorder.setApplicationMigrationClasses(javaMigrationClasses);

final Map<String, Collection<Callback>> callbacks = FlywayCallbacksLocator.with(
dataSourceNames,
flywayBuildTimeConfig,
flywayBuildItems,
combinedIndexBuildItem,
reflectiveClassProducer).getCallbacks();
recorder.setApplicationCallbackClasses(callbacks);

resourceProducer.produce(new NativeImageResourceBuildItem(applicationMigrations.toArray(new String[0])));
return new MigrationStateBuildItem(datasourcesWithMigrations, datasourcesWithoutMigrations);
return new MigrationStateBuildItem(instancesWithMigrations, instancesWithoutMigrations);
}

@SuppressWarnings("unchecked")
Expand All @@ -187,60 +223,40 @@ private void addJavaMigrations(Collection<ClassInfo> candidates, RecorderContext
@Consume(LoggingSetupBuildItem.class)
@Record(ExecutionTime.RUNTIME_INIT)
void createBeans(FlywayRecorder recorder,
List<JdbcDataSourceBuildItem> jdbcDataSourceBuildItems,
List<JdbcInitialSQLGeneratorBuildItem> sqlGeneratorBuildItems,
BuildProducer<AdditionalBeanBuildItem> additionalBeans,
List<FlywayBuildItem> flywayBuildItems,
BuildProducer<SyntheticBeanBuildItem> syntheticBeanBuildItemBuildProducer,
MigrationStateBuildItem migrationsBuildItem,
List<JdbcInitialSQLGeneratorBuildItem> sqlGeneratorBuildItems,
FlywayBuildTimeConfig flywayBuildTimeConfig) {
// make a FlywayContainerProducer bean
additionalBeans.produce(AdditionalBeanBuildItem.builder().addBeanClasses(FlywayContainerProducer.class).setUnremovable()
.setDefaultScope(DotNames.SINGLETON).build());
// add the @FlywayDataSource class otherwise it won't be registered as a qualifier
additionalBeans.produce(AdditionalBeanBuildItem.builder().addBeanClass(FlywayDataSource.class).build());

Collection<String> dataSourceNames = getDataSourceNames(jdbcDataSourceBuildItems);
for (FlywayBuildItem flywayBuildItem : flywayBuildItems) {

for (String dataSourceName : dataSourceNames) {
boolean hasMigrations = migrationsBuildItem.hasMigrations.contains(dataSourceName);
boolean hasMigrations = migrationsBuildItem.hasMigrations.contains(flywayBuildItem.getId());
boolean createPossible = false;
if (!hasMigrations) {
createPossible = sqlGeneratorBuildItems.stream().anyMatch(s -> s.getDatabaseName().equals(dataSourceName));
createPossible = sqlGeneratorBuildItems.stream()
.anyMatch(s -> s.getDatabaseName().equals(flywayBuildItem.getDataSourceName()));
}

SyntheticBeanBuildItem.ExtendedBeanConfigurator flywayContainerConfigurator = SyntheticBeanBuildItem
.configure(FlywayContainer.class)
.scope(Singleton.class)
.setRuntimeInit()
.unremovable()
.addInjectionPoint(ClassType.create(DotName.createSimple(FlywayContainerProducer.class)))
.addQualifier(flywayBuildItem.getQualifier())
.priority(flywayBuildItem.getPriority())
.addInjectionPoint(ClassType.create(DotName.createSimple(flywayBuildItem.getProducerClass())))
.addInjectionPoint(ClassType.create(DotName.createSimple(DataSource.class)),
AgroalDataSourceBuildUtil.qualifier(dataSourceName))
AgroalDataSourceBuildUtil.qualifier(flywayBuildItem.getDataSourceName()))
.startup()
.checkActive(recorder.flywayCheckActiveSupplier(dataSourceName))
.createWith(recorder.flywayContainerFunction(dataSourceName, hasMigrations, createPossible));

AnnotationInstance flywayContainerQualifier;

if (DataSourceUtil.isDefault(dataSourceName)) {
flywayContainerConfigurator.addQualifier(Default.class);

// Flyway containers used to be ordered with the default database coming first.
// Some multitenant tests are relying on this order.
flywayContainerConfigurator.priority(10);

flywayContainerQualifier = AnnotationInstance.builder(Default.class).build();
} else {
String beanName = FLYWAY_CONTAINER_BEAN_NAME_PREFIX + dataSourceName;
flywayContainerConfigurator.name(beanName);

flywayContainerConfigurator.addQualifier().annotation(DotNames.NAMED).addValue("value", beanName).done();
flywayContainerConfigurator.addQualifier().annotation(FlywayDataSource.class).addValue("value", dataSourceName)
.done();
flywayContainerConfigurator.priority(5);

flywayContainerQualifier = AnnotationInstance.builder(FlywayDataSource.class).add("value", dataSourceName)
.build();
.checkActive(recorder.flywayCheckActiveSupplier(flywayBuildItem.getDataSourceName()))
.createWith(recorder.flywayContainerFunction(flywayBuildItem.getProducerClass(),
flywayBuildItem.getDataSourceName(), flywayBuildItem.getName(), hasMigrations, createPossible));

if (flywayBuildItem.getContainerBeanName() != null) {
flywayContainerConfigurator.name(flywayBuildItem.getContainerBeanName());
flywayContainerConfigurator.addQualifier().annotation(DotNames.NAMED)
.addValue("value", flywayBuildItem.getContainerBeanName()).done();
}

syntheticBeanBuildItemBuildProducer.produce(flywayContainerConfigurator.done());
Expand All @@ -250,21 +266,19 @@ void createBeans(FlywayRecorder recorder,
.scope(Singleton.class)
.setRuntimeInit()
.unremovable()
.addInjectionPoint(ClassType.create(DotName.createSimple(FlywayContainer.class)), flywayContainerQualifier)
.addQualifier(flywayBuildItem.getQualifier())
.priority(flywayBuildItem.getPriority())
.addInjectionPoint(ClassType.create(DotName.createSimple(flywayBuildItem.getProducerClass())))
.addInjectionPoint(ClassType.create(DotName.createSimple(FlywayContainer.class)),
flywayBuildItem.getQualifier())
.startup()
.checkActive(recorder.flywayCheckActiveSupplier(dataSourceName))
.createWith(recorder.flywayFunction(dataSourceName));
.checkActive(recorder.flywayCheckActiveSupplier(flywayBuildItem.getDataSourceName()))
.createWith(recorder.flywayFunction(flywayBuildItem.getProducerClass(), flywayBuildItem.getName()));

if (DataSourceUtil.isDefault(dataSourceName)) {
flywayConfigurator.addQualifier(Default.class);
flywayConfigurator.priority(10);
} else {
String beanName = FLYWAY_BEAN_NAME_PREFIX + dataSourceName;
flywayConfigurator.name(beanName);
flywayConfigurator.priority(5);

flywayConfigurator.addQualifier().annotation(DotNames.NAMED).addValue("value", beanName).done();
flywayConfigurator.addQualifier().annotation(FlywayDataSource.class).addValue("value", dataSourceName).done();
if (flywayBuildItem.getFlywayBeanName() != null) {
flywayConfigurator.name(flywayBuildItem.getFlywayBeanName());
flywayConfigurator.addQualifier().annotation(DotNames.NAMED)
.addValue("value", flywayBuildItem.getFlywayBeanName()).done();
}

syntheticBeanBuildItemBuildProducer.produce(flywayConfigurator.done());
Expand All @@ -278,13 +292,11 @@ public ServiceStartBuildItem startActions(FlywayRecorder recorder,
FlywayRuntimeConfig config,
BuildProducer<JdbcDataSourceSchemaReadyBuildItem> schemaReadyBuildItem,
BuildProducer<InitTaskCompletedBuildItem> initializationCompleteBuildItem,
List<JdbcDataSourceBuildItem> jdbcDataSourceBuildItems,
List<FlywayBuildItem> flywayBuildItems,
MigrationStateBuildItem migrationsBuildItem) {

Collection<String> dataSourceNames = getDataSourceNames(jdbcDataSourceBuildItems);

for (String dataSourceName : dataSourceNames) {
recorder.doStartActions(dataSourceName);
for (FlywayBuildItem flywayBuildItem : flywayBuildItems) {
recorder.doStartActions(flywayBuildItem.getDataSourceName());
}

// once we are done running the migrations, we produce a build item indicating that the
Expand Down
Loading

0 comments on commit 0838b2c

Please sign in to comment.