From 8fe1856d359402e82be5948b91cfd625e5fde67f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 16 Jul 2024 16:09:58 +0200 Subject: [PATCH] Fail on startup when inactive datasources are injected By reimplementing Datasource eager startup through Arc's `startup` feature, and active/inactive through Arc's `checkActive` feature. --- .../deployment/AgroalDataSourceBuildUtil.java | 21 +++++++ .../agroal/deployment/AgroalProcessor.java | 9 +-- ...asourceProgrammaticallyRetrievedTest.java} | 46 ++------------ ...faultDatasourceStaticallyInjectedTest.java | 50 +++++++++++++++ ...asourceProgrammaticallyRetrievedTest.java} | 43 ++----------- ...NamedDatasourceStaticallyInjectedTest.java | 54 ++++++++++++++++ .../quarkus/agroal/test/EagerStartupTest.java | 18 +++--- .../agroal/runtime/AgroalDataSourceUtil.java | 50 +++++++++++++++ .../runtime/AgroalDataSourcesInitializer.java | 19 ------ .../agroal/runtime/AgroalRecorder.java | 25 +++++++- .../quarkus/agroal/runtime/DataSources.java | 62 +++++++------------ .../runtime/health/DataSourceHealthCheck.java | 20 +++--- .../metrics/AgroalMetricsRecorder.java | 14 ++--- .../datasource/runtime/DataSourceSupport.java | 6 ++ .../flyway/deployment/FlywayProcessor.java | 6 +- .../flyway/runtime/FlywayContainerUtil.java | 4 +- .../flyway/runtime/FlywayRecorder.java | 15 ++--- .../deployment/HibernateOrmCdiProcessor.java | 4 +- .../FastBootHibernatePersistenceProvider.java | 4 +- .../deployment/LiquibaseProcessor.java | 7 ++- .../runtime/LiquibaseFactoryUtil.java | 4 +- .../liquibase/runtime/LiquibaseRecorder.java | 28 +++++---- 22 files changed, 308 insertions(+), 201 deletions(-) create mode 100644 extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalDataSourceBuildUtil.java rename extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/{ConfigActiveFalseDefaultDatasourceTest.java => ConfigActiveFalseDefaultDatasourceProgrammaticallyRetrievedTest.java} (61%) create mode 100644 extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseDefaultDatasourceStaticallyInjectedTest.java rename extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/{ConfigActiveFalseNamedDatasourceTest.java => ConfigActiveFalseNamedDatasourceProgrammaticallyRetrievedTest.java} (65%) create mode 100644 extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseNamedDatasourceStaticallyInjectedTest.java create mode 100644 extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalDataSourceUtil.java delete mode 100644 extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalDataSourcesInitializer.java diff --git a/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalDataSourceBuildUtil.java b/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalDataSourceBuildUtil.java new file mode 100644 index 00000000000000..b3324a4118acca --- /dev/null +++ b/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalDataSourceBuildUtil.java @@ -0,0 +1,21 @@ +package io.quarkus.agroal.deployment; + +import jakarta.enterprise.inject.Default; + +import org.jboss.jandex.AnnotationInstance; + +import io.quarkus.agroal.DataSource; +import io.quarkus.datasource.common.runtime.DataSourceUtil; + +public final class AgroalDataSourceBuildUtil { + private AgroalDataSourceBuildUtil() { + } + + public static AnnotationInstance qualifier(String dataSourceName) { + if (dataSourceName == null || DataSourceUtil.isDefault(dataSourceName)) { + return AnnotationInstance.builder(Default.class).build(); + } else { + return AnnotationInstance.builder(DataSource.class).value(dataSourceName).build(); + } + } +} diff --git a/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java b/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java index dd17a2b4873380..8fd0156e1055eb 100644 --- a/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java +++ b/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java @@ -25,7 +25,6 @@ import io.agroal.api.AgroalPoolInterceptor; import io.quarkus.agroal.DataSource; import io.quarkus.agroal.runtime.AgroalDataSourceSupport; -import io.quarkus.agroal.runtime.AgroalDataSourcesInitializer; import io.quarkus.agroal.runtime.AgroalRecorder; import io.quarkus.agroal.runtime.DataSourceJdbcBuildTimeConfig; import io.quarkus.agroal.runtime.DataSources; @@ -35,6 +34,7 @@ import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; import io.quarkus.agroal.spi.JdbcDriverBuildItem; import io.quarkus.agroal.spi.OpenTelemetryInitBuildItem; +import io.quarkus.arc.BeanDestroyer; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.arc.deployment.UnremovableBeanBuildItem; @@ -241,8 +241,6 @@ void generateDataSourceSupportBean(AgroalRecorder recorder, .setDefaultScope(DotNames.SINGLETON).build()); // add the @DataSource class otherwise it won't be registered as a qualifier additionalBeans.produce(AdditionalBeanBuildItem.builder().addBeanClass(DataSource.class).build()); - // make sure datasources are initialized at startup - additionalBeans.produce(new AdditionalBeanBuildItem(AgroalDataSourcesInitializer.class)); // make AgroalPoolInterceptor beans unremovable, users still have to make them beans unremovableBeans.produce(UnremovableBeanBuildItem.beanTypes(AgroalPoolInterceptor.class)); @@ -285,9 +283,12 @@ void generateDataSourceBeans(AgroalRecorder recorder, .setRuntimeInit() .unremovable() .addInjectionPoint(ClassType.create(DotName.createSimple(DataSources.class))) + .startup() + .checkActive(recorder.agroalDataSourceActiveChecker(dataSourceName)) // pass the runtime config into the recorder to ensure that the DataSource related beans // are created after runtime configuration has been set up - .createWith(recorder.agroalDataSourceSupplier(dataSourceName, dataSourcesRuntimeConfig)); + .createWith(recorder.agroalDataSourceSupplier(dataSourceName, dataSourcesRuntimeConfig)) + .destroyer(BeanDestroyer.AutoCloseableDestroyer.class); if (aggregatedBuildTimeConfigBuildItem.isDefault()) { configurator.addQualifier(Default.class); diff --git a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseDefaultDatasourceTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseDefaultDatasourceProgrammaticallyRetrievedTest.java similarity index 61% rename from extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseDefaultDatasourceTest.java rename to extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseDefaultDatasourceProgrammaticallyRetrievedTest.java index 4e71a02d8503ed..4fd83c4650ba45 100644 --- a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseDefaultDatasourceTest.java +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseDefaultDatasourceProgrammaticallyRetrievedTest.java @@ -3,44 +3,33 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import java.sql.SQLException; - import javax.sql.DataSource; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.CreationException; -import jakarta.inject.Inject; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import io.agroal.api.AgroalDataSource; import io.quarkus.arc.Arc; -import io.quarkus.runtime.configuration.ConfigurationException; +import io.quarkus.arc.InactiveBeanException; import io.quarkus.test.QuarkusUnitTest; -public class ConfigActiveFalseDefaultDatasourceTest { +public class ConfigActiveFalseDefaultDatasourceProgrammaticallyRetrievedTest { @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() .overrideConfigKey("quarkus.datasource.active", "false"); - @Inject - MyBean myBean; - @Test public void dataSource() { DataSource ds = Arc.container().instance(DataSource.class).get(); // The bean is always available to be injected during static init // since we don't know whether the datasource will be active at runtime. - // So the bean cannot be null. + // So the bean proxy cannot be null. assertThat(ds).isNotNull(); // However, any attempt to use it at runtime will fail. assertThatThrownBy(() -> ds.getConnection()) - .isInstanceOf(RuntimeException.class) - .cause() - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(InactiveBeanException.class) .hasMessageContainingAll("Datasource '' was deactivated through configuration properties.", "To solve this, avoid accessing this datasource at runtime, for instance by deactivating consumers (persistence units, ...).", "Alternatively, activate the datasource by setting configuration property 'quarkus.datasource.active'" @@ -54,38 +43,15 @@ public void agroalDataSource() { // The bean is always available to be injected during static init // since we don't know whether the datasource will be active at runtime. - // So the bean cannot be null. + // So the bean proxy cannot be null. assertThat(ds).isNotNull(); // However, any attempt to use it at runtime will fail. assertThatThrownBy(() -> ds.getConnection()) - .isInstanceOf(RuntimeException.class) - .cause() - .isInstanceOf(ConfigurationException.class) - .hasMessageContainingAll("Datasource '' was deactivated through configuration properties.", - "To solve this, avoid accessing this datasource at runtime, for instance by deactivating consumers (persistence units, ...).", - "Alternatively, activate the datasource by setting configuration property 'quarkus.datasource.active'" - + " to 'true' and configure datasource ''", - "Refer to https://quarkus.io/guides/datasource for guidance."); - } - - @Test - public void injectedBean() { - assertThatThrownBy(() -> myBean.useDatasource()) - .isInstanceOf(CreationException.class) + .isInstanceOf(InactiveBeanException.class) .hasMessageContainingAll("Datasource '' was deactivated through configuration properties.", "To solve this, avoid accessing this datasource at runtime, for instance by deactivating consumers (persistence units, ...).", "Alternatively, activate the datasource by setting configuration property 'quarkus.datasource.active'" + " to 'true' and configure datasource ''", "Refer to https://quarkus.io/guides/datasource for guidance."); } - - @ApplicationScoped - public static class MyBean { - @Inject - DataSource ds; - - public void useDatasource() throws SQLException { - ds.getConnection(); - } - } } diff --git a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseDefaultDatasourceStaticallyInjectedTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseDefaultDatasourceStaticallyInjectedTest.java new file mode 100644 index 00000000000000..acb5b7c6ccdd57 --- /dev/null +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseDefaultDatasourceStaticallyInjectedTest.java @@ -0,0 +1,50 @@ +package io.quarkus.agroal.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.sql.SQLException; + +import javax.sql.DataSource; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.InactiveBeanException; +import io.quarkus.test.QuarkusUnitTest; + +public class ConfigActiveFalseDefaultDatasourceStaticallyInjectedTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .overrideConfigKey("quarkus.datasource.active", "false") + .assertException(e -> assertThat(e).isInstanceOf(InactiveBeanException.class) + .hasMessageContainingAll("Datasource '' was deactivated through configuration properties.", + "To solve this, avoid accessing this datasource at runtime, for instance by deactivating consumers (persistence units, ...).", + "Alternatively, activate the datasource by setting configuration property 'quarkus.datasource.active'" + + " to 'true' and configure datasource ''", + "Refer to https://quarkus.io/guides/datasource for guidance.", + "This bean is injected into", + MyBean.class.getName() + "#ds")); + + @Inject + MyBean myBean; + + @Test + public void test() { + Assertions.fail("Startup should have failed"); + } + + @ApplicationScoped + public static class MyBean { + @Inject + DataSource ds; + + public void useDatasource() throws SQLException { + ds.getConnection(); + } + } +} diff --git a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseNamedDatasourceTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseNamedDatasourceProgrammaticallyRetrievedTest.java similarity index 65% rename from extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseNamedDatasourceTest.java rename to extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseNamedDatasourceProgrammaticallyRetrievedTest.java index 812aeadd025143..d79de012f0af32 100644 --- a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseNamedDatasourceTest.java +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseNamedDatasourceProgrammaticallyRetrievedTest.java @@ -3,22 +3,16 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import java.sql.SQLException; - import javax.sql.DataSource; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.CreationException; -import jakarta.inject.Inject; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.arc.Arc; -import io.quarkus.runtime.configuration.ConfigurationException; +import io.quarkus.arc.InactiveBeanException; import io.quarkus.test.QuarkusUnitTest; -public class ConfigActiveFalseNamedDatasourceTest { +public class ConfigActiveFalseNamedDatasourceProgrammaticallyRetrievedTest { @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() @@ -27,9 +21,6 @@ public class ConfigActiveFalseNamedDatasourceTest { // otherwise it's considered unconfigured at build time... .overrideConfigKey("quarkus.datasource.users.db-kind", "h2"); - @Inject - MyBean myBean; - @Test public void dataSource() { DataSource ds = Arc.container().instance(DataSource.class, @@ -41,9 +32,7 @@ public void dataSource() { assertThat(ds).isNotNull(); // However, any attempt to use it at runtime will fail. assertThatThrownBy(() -> ds.getConnection()) - .isInstanceOf(RuntimeException.class) - .cause() - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(InactiveBeanException.class) .hasMessageContainingAll("Datasource 'users' was deactivated through configuration properties.", "To solve this, avoid accessing this datasource at runtime, for instance by deactivating consumers (persistence units, ...).", "Alternatively, activate the datasource by setting configuration property 'quarkus.datasource.\"users\".active'" @@ -62,35 +51,11 @@ public void agroalDataSource() { assertThat(ds).isNotNull(); // However, any attempt to use it at runtime will fail. assertThatThrownBy(() -> ds.getConnection()) - .isInstanceOf(RuntimeException.class) - .cause() - .isInstanceOf(ConfigurationException.class) - .hasMessageContainingAll("Datasource 'users' was deactivated through configuration properties.", - "To solve this, avoid accessing this datasource at runtime, for instance by deactivating consumers (persistence units, ...).", - "Alternatively, activate the datasource by setting configuration property 'quarkus.datasource.\"users\".active'" - + " to 'true' and configure datasource 'users'", - "Refer to https://quarkus.io/guides/datasource for guidance."); - } - - @Test - public void injectedBean() { - assertThatThrownBy(() -> myBean.useDatasource()) - .isInstanceOf(CreationException.class) + .isInstanceOf(InactiveBeanException.class) .hasMessageContainingAll("Datasource 'users' was deactivated through configuration properties.", "To solve this, avoid accessing this datasource at runtime, for instance by deactivating consumers (persistence units, ...).", "Alternatively, activate the datasource by setting configuration property 'quarkus.datasource.\"users\".active'" + " to 'true' and configure datasource 'users'", "Refer to https://quarkus.io/guides/datasource for guidance."); } - - @ApplicationScoped - public static class MyBean { - @Inject - @io.quarkus.agroal.DataSource("users") - DataSource ds; - - public void useDatasource() throws SQLException { - ds.getConnection(); - } - } } diff --git a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseNamedDatasourceStaticallyInjectedTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseNamedDatasourceStaticallyInjectedTest.java new file mode 100644 index 00000000000000..9e1068b5e3aabe --- /dev/null +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseNamedDatasourceStaticallyInjectedTest.java @@ -0,0 +1,54 @@ +package io.quarkus.agroal.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.sql.SQLException; + +import javax.sql.DataSource; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.InactiveBeanException; +import io.quarkus.test.QuarkusUnitTest; + +public class ConfigActiveFalseNamedDatasourceStaticallyInjectedTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .overrideConfigKey("quarkus.datasource.users.active", "false") + // We need at least one build-time property for the datasource, + // otherwise it's considered unconfigured at build time... + .overrideConfigKey("quarkus.datasource.users.db-kind", "h2") + .assertException(e -> assertThat(e).isInstanceOf(InactiveBeanException.class) + .hasMessageContainingAll("Datasource 'users' was deactivated through configuration properties.", + "To solve this, avoid accessing this datasource at runtime, for instance by deactivating consumers (persistence units, ...).", + "Alternatively, activate the datasource by setting configuration property 'quarkus.datasource.active'" + + " to 'true' and configure datasource 'users'", + "Refer to https://quarkus.io/guides/datasource for guidance.", + "This bean is injected into", + MyBean.class.getName() + "#ds")); + + @Inject + MyBean myBean; + + @Test + public void test() { + Assertions.fail("Startup should have failed"); + } + + @ApplicationScoped + public static class MyBean { + @Inject + @io.quarkus.agroal.DataSource("users") + DataSource ds; + + public void useDatasource() throws SQLException { + ds.getConnection(); + } + } +} diff --git a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/EagerStartupTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/EagerStartupTest.java index 838c8ad9a71315..f5c4efe44ba05d 100644 --- a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/EagerStartupTest.java +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/EagerStartupTest.java @@ -2,12 +2,13 @@ import static org.assertj.core.api.Assertions.assertThat; -import jakarta.inject.Singleton; +import jakarta.enterprise.context.ApplicationScoped; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import io.quarkus.agroal.runtime.DataSources; +import io.agroal.api.AgroalDataSource; +import io.quarkus.agroal.runtime.AgroalDataSourceUtil; import io.quarkus.arc.Arc; import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.test.QuarkusUnitTest; @@ -26,16 +27,13 @@ public class EagerStartupTest { @Test public void shouldStartEagerly() { var container = Arc.container(); - var instanceHandle = container.instance(DataSources.class); - // Check that the following call won't trigger a lazy initialization: - // the DataSources bean must be eagerly initialized. - assertThat(container.getActiveContext(Singleton.class).getState() + var instanceHandle = container.instance(AgroalDataSource.class, + AgroalDataSourceUtil.qualifier(DataSourceUtil.DEFAULT_DATASOURCE_NAME)); + // Check that the datasource has already been eagerly created. + assertThat(container.getActiveContext(ApplicationScoped.class).getState() .getContextualInstances().get(instanceHandle.getBean())) - .as("Eagerly instantiated DataSources bean") + .as("Eagerly instantiated DataSource bean") .isNotNull(); - // Check that the datasource has already been eagerly created. - assertThat(instanceHandle.get().isDataSourceCreated(DataSourceUtil.DEFAULT_DATASOURCE_NAME)) - .isTrue(); } } diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalDataSourceUtil.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalDataSourceUtil.java new file mode 100644 index 00000000000000..044ad5ad0330ae --- /dev/null +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalDataSourceUtil.java @@ -0,0 +1,50 @@ +package io.quarkus.agroal.runtime; + +import java.lang.annotation.Annotation; +import java.util.LinkedHashSet; +import java.util.Optional; +import java.util.Set; + +import jakarta.enterprise.inject.Default; + +import io.agroal.api.AgroalDataSource; +import io.quarkus.agroal.DataSource; +import io.quarkus.arc.Arc; +import io.quarkus.arc.ClientProxy; +import io.quarkus.arc.InactiveBeanException; +import io.quarkus.arc.InjectableInstance; +import io.quarkus.datasource.common.runtime.DataSourceUtil; + +public final class AgroalDataSourceUtil { + private AgroalDataSourceUtil() { + } + + public static InjectableInstance dataSourceInstance(String dataSourceName) { + return Arc.container().select(AgroalDataSource.class, qualifier(dataSourceName)); + } + + public static Optional dataSourceIfActive(String dataSourceName) { + try { + AgroalDataSource instance = dataSourceInstance(dataSourceName).get(); + ClientProxy.unwrap(instance); + return Optional.of(instance); + } catch (InactiveBeanException e) { + return Optional.empty(); + } + } + + public static Set activeDataSourceNames() { + Set activeNames = new LinkedHashSet<>( + Arc.container().select(AgroalDataSourceSupport.class).get().entries.keySet()); + activeNames.removeIf(name -> dataSourceIfActive(name).isEmpty()); + return activeNames; + } + + public static Annotation qualifier(String dataSourceName) { + if (dataSourceName == null || DataSourceUtil.isDefault(dataSourceName)) { + return Default.Literal.INSTANCE; + } else { + return new DataSource.DataSourceLiteral(dataSourceName); + } + } +} diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalDataSourcesInitializer.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalDataSourcesInitializer.java deleted file mode 100644 index 53fe4ae0c9784b..00000000000000 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalDataSourcesInitializer.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.quarkus.agroal.runtime; - -import java.util.List; - -import jakarta.enterprise.event.Observes; - -import io.agroal.api.AgroalDataSource; -import io.quarkus.arc.All; -import io.quarkus.runtime.StartupEvent; - -/** - * Make sure datasources are initialized at startup. - */ -public class AgroalDataSourcesInitializer { - - void init(@Observes StartupEvent event, @All List dataSources) { - // nothing to do here, eager injection will initialize the beans - } -} diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalRecorder.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalRecorder.java index 52b7d1a20907d0..1cacdfbb58fb7c 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalRecorder.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalRecorder.java @@ -3,14 +3,25 @@ import java.util.function.Function; import java.util.function.Supplier; +import jakarta.inject.Inject; + import io.agroal.api.AgroalDataSource; import io.quarkus.arc.SyntheticCreationalContext; +import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.datasource.runtime.DataSourcesRuntimeConfig; +import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.annotations.Recorder; @Recorder public class AgroalRecorder { + private final RuntimeValue runtimeConfig; + + @Inject + public AgroalRecorder(RuntimeValue runtimeConfig) { + this.runtimeConfig = runtimeConfig; + } + public Supplier dataSourceSupportSupplier(AgroalDataSourceSupport agroalDataSourceSupport) { return new Supplier() { @Override @@ -20,14 +31,26 @@ public AgroalDataSourceSupport get() { }; } + public Runnable agroalDataSourceActiveChecker(String dataSourceName) { + return new Runnable() { + @Override + public void run() { + if (!runtimeConfig.getValue().dataSources().get(dataSourceName).active()) { + throw DataSourceUtil.dataSourceInactive(dataSourceName); + } + } + }; + } + public Function, AgroalDataSource> agroalDataSourceSupplier( String dataSourceName, @SuppressWarnings("unused") DataSourcesRuntimeConfig dataSourcesRuntimeConfig) { return new Function<>() { + @SuppressWarnings("deprecation") @Override public AgroalDataSource apply(SyntheticCreationalContext context) { DataSources dataSources = context.getInjectedReference(DataSources.class); - return dataSources.getDataSource(dataSourceName); + return dataSources.createDataSource(dataSourceName); } }; } diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSources.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSources.java index a8b005fc4aa538..7a5298ee04dd12 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSources.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSources.java @@ -11,13 +11,10 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.function.Function; import java.util.stream.Collectors; -import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import jakarta.enterprise.inject.Any; -import jakarta.enterprise.inject.Default; import jakarta.enterprise.inject.Instance; import jakarta.inject.Singleton; import jakarta.transaction.TransactionManager; @@ -39,7 +36,6 @@ import io.agroal.api.security.SimplePassword; import io.agroal.api.transaction.TransactionIntegration; import io.agroal.narayana.NarayanaTransactionIntegration; -import io.quarkus.agroal.DataSource; import io.quarkus.agroal.runtime.JdbcDriver.JdbcDriverLiteral; import io.quarkus.arc.Arc; import io.quarkus.credentials.CredentialsProvider; @@ -59,7 +55,12 @@ * The {@code createDataSource} method is called at runtime (see * {@link AgroalRecorder#agroalDataSourceSupplier(String, DataSourcesRuntimeConfig)}) * in order to produce the actual {@code AgroalDataSource} objects. + * + * @deprecated This class should not be used from applications or other extensions. + * For applications, use CDI to retrieve datasources instead. + * For extensions, use {@link AgroalDataSourceUtil} instead. */ +@Deprecated @Singleton public class DataSources { @@ -120,47 +121,42 @@ public DataSources(DataSourcesBuildTimeConfig dataSourcesBuildTimeConfig, *

* This method is thread-safe * - * @deprecated This method should not be used as it can very easily lead to timing issues during bean creation + * @deprecated Use {@link AgroalDataSourceUtil#dataSourceInstance(String)} instead. + * This method should not be used as it can very easily lead to timing issues during bean creation. */ @Deprecated public static AgroalDataSource fromName(String dataSourceName) { - return Arc.container().instance(DataSources.class).get() - .getDataSource(dataSourceName); + return AgroalDataSourceUtil.dataSourceInstance(dataSourceName).get(); } + /** + * @deprecated Use {@link AgroalDataSourceUtil#dataSourceIfActive(String)} instead. + */ + @Deprecated public boolean isDataSourceCreated(String dataSourceName) { return dataSources.containsKey(dataSourceName); } + /** + * @deprecated Use {@link AgroalDataSourceUtil#activeDataSourceNames()} instead. + */ + @Deprecated public Set getActiveDataSourceNames() { // Datasources are created on startup, // and we only create active datasources. return dataSources.keySet(); } + /** + * @deprecated Use {@link AgroalDataSourceUtil#dataSourceInstance(String)} instead. + */ + @Deprecated public AgroalDataSource getDataSource(String dataSourceName) { - return dataSources.computeIfAbsent(dataSourceName, new Function() { - @Override - public AgroalDataSource apply(String s) { - return doCreateDataSource(s, true); - } - }); - } - - @PostConstruct - public void start() { - for (String dataSourceName : agroalDataSourceSupport.entries.keySet()) { - dataSources.computeIfAbsent(dataSourceName, new Function() { - @Override - public AgroalDataSource apply(String s) { - return doCreateDataSource(s, false); - } - }); - } + return AgroalDataSourceUtil.dataSourceInstance(dataSourceName).get(); } @SuppressWarnings("resource") - public AgroalDataSource doCreateDataSource(String dataSourceName, boolean failIfInactive) { + public AgroalDataSource createDataSource(String dataSourceName) { if (!agroalDataSourceSupport.entries.containsKey(dataSourceName)) { throw new IllegalArgumentException("No datasource named '" + dataSourceName + "' exists"); } @@ -169,16 +165,6 @@ public AgroalDataSource doCreateDataSource(String dataSourceName, boolean failIf .dataSources().get(dataSourceName).jdbc(); DataSourceRuntimeConfig dataSourceRuntimeConfig = dataSourcesRuntimeConfig.dataSources().get(dataSourceName); - if (dataSourceSupport.getInactiveNames().contains(dataSourceName)) { - if (failIfInactive) { - throw DataSourceUtil.dataSourceInactive(dataSourceName); - } else { - // This only happens on startup, and effectively cancels the creation - // so that we only throw an exception on first actual use. - return null; - } - } - DataSourceJdbcRuntimeConfig dataSourceJdbcRuntimeConfig = dataSourcesJdbcRuntimeConfig .dataSources().get(dataSourceName).jdbc(); if (!dataSourceJdbcRuntimeConfig.url().isPresent()) { @@ -290,9 +276,7 @@ public AgroalDataSource doCreateDataSource(String dataSourceName, boolean failIf // Set pool interceptors for this datasource Collection interceptorList = agroalPoolInterceptors - .select(dataSourceName == null || DataSourceUtil.isDefault(dataSourceName) - ? Default.Literal.INSTANCE - : new DataSource.DataSourceLiteral(dataSourceName)) + .select(AgroalDataSourceUtil.qualifier(dataSourceName)) .stream().collect(Collectors.toList()); if (!interceptorList.isEmpty()) { dataSource.setPoolInterceptors(interceptorList); diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/health/DataSourceHealthCheck.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/health/DataSourceHealthCheck.java index 4a6e3db63be68c..5fdc1975cabb6e 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/health/DataSourceHealthCheck.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/health/DataSourceHealthCheck.java @@ -3,6 +3,7 @@ import java.sql.SQLException; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.Set; import javax.sql.DataSource; @@ -19,7 +20,7 @@ import io.agroal.api.AgroalDataSource; import io.quarkus.agroal.runtime.AgroalDataSourceSupport; -import io.quarkus.agroal.runtime.DataSources; +import io.quarkus.agroal.runtime.AgroalDataSourceUtil; import io.quarkus.arc.Arc; import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.datasource.runtime.DataSourceSupport; @@ -29,28 +30,27 @@ public class DataSourceHealthCheck implements HealthCheck { @Inject - Instance dataSources; + Instance dataSourceSupport; private final Map checkedDataSources = new HashMap<>(); @PostConstruct protected void init() { - if (!dataSources.isResolvable()) { + if (!dataSourceSupport.isResolvable()) { // No configured Agroal datasource at build time. return; } - DataSourceSupport support = Arc.container().instance(DataSourceSupport.class) - .get(); + DataSourceSupport support = dataSourceSupport.get(); AgroalDataSourceSupport agroalSupport = Arc.container().instance(AgroalDataSourceSupport.class) .get(); - Set excludedNames = support.getInactiveOrHealthCheckExcludedNames(); + Set healthCheckExcludedNames = support.getHealthCheckExcludedNames(); for (String name : agroalSupport.entries.keySet()) { - if (excludedNames.contains(name)) { + if (healthCheckExcludedNames.contains(name)) { continue; } - DataSource ds = dataSources.get().getDataSource(name); - if (ds != null) { - checkedDataSources.put(name, ds); + Optional dataSource = AgroalDataSourceUtil.dataSourceIfActive(name); + if (dataSource.isPresent()) { + checkedDataSources.put(name, dataSource.get()); } } } diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/metrics/AgroalMetricsRecorder.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/metrics/AgroalMetricsRecorder.java index 726a0c0512e5a3..92592130f9ed3c 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/metrics/AgroalMetricsRecorder.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/metrics/AgroalMetricsRecorder.java @@ -1,15 +1,16 @@ package io.quarkus.agroal.runtime.metrics; import java.time.Duration; +import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import org.jboss.logging.Logger; +import io.agroal.api.AgroalDataSource; import io.agroal.api.AgroalDataSourceMetrics; -import io.quarkus.agroal.runtime.DataSources; -import io.quarkus.arc.Arc; +import io.quarkus.agroal.runtime.AgroalDataSourceUtil; import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.runtime.annotations.Recorder; import io.quarkus.runtime.metrics.MetricsFactory; @@ -34,15 +35,14 @@ public Consumer registerDataSourceMetrics(String dataSourceName) return new Consumer() { @Override public void accept(MetricsFactory metricsFactory) { - DataSources dataSources = Arc.container().instance(DataSources.class).get(); - if (!dataSources.getActiveDataSourceNames().contains(dataSourceName)) { - log.debug("Not registering metrics for datasource '" + dataSourceName + "'" - + " as the datasource has been deactivated in the configuration"); + Optional dataSource = AgroalDataSourceUtil.dataSourceIfActive(dataSourceName); + if (dataSource.isEmpty()) { + log.debug("Not registering metrics for datasource '" + dataSourceName + "' as the datasource is inactive"); return; } String tagValue = DataSourceUtil.isDefault(dataSourceName) ? "default" : dataSourceName; - AgroalDataSourceMetrics metrics = dataSources.getDataSource(dataSourceName).getMetrics(); + AgroalDataSourceMetrics metrics = dataSource.get().getMetrics(); metricsFactory.builder("agroal.active.count") .description( diff --git a/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourceSupport.java b/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourceSupport.java index 21ea9141a98cea..054ba665639200 100644 --- a/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourceSupport.java +++ b/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourceSupport.java @@ -12,11 +12,13 @@ */ public class DataSourceSupport { + private final Set healthCheckExcludedNames; private final Set inactiveNames; private final Set inactiveOrHealthCheckExcludedNames; public DataSourceSupport(Set healthCheckExcludedNames, Set inactiveNames) { + this.healthCheckExcludedNames = healthCheckExcludedNames; this.inactiveOrHealthCheckExcludedNames = new HashSet<>(); inactiveOrHealthCheckExcludedNames.addAll(inactiveNames); inactiveOrHealthCheckExcludedNames.addAll(healthCheckExcludedNames); @@ -30,4 +32,8 @@ public Set getInactiveNames() { public Set getInactiveOrHealthCheckExcludedNames() { return inactiveOrHealthCheckExcludedNames; } + + public Set getHealthCheckExcludedNames() { + return healthCheckExcludedNames; + } } diff --git a/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/deployment/FlywayProcessor.java b/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/deployment/FlywayProcessor.java index 4f4ba14fe7245a..95b7c1b562f008 100644 --- a/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/deployment/FlywayProcessor.java +++ b/extensions/flyway/deployment/src/main/java/io/quarkus/flyway/deployment/FlywayProcessor.java @@ -32,7 +32,8 @@ import org.jboss.jandex.DotName; import org.jboss.logging.Logger; -import io.quarkus.agroal.runtime.DataSources; +import io.agroal.api.AgroalDataSource; +import io.quarkus.agroal.deployment.AgroalDataSourceBuildUtil; import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; import io.quarkus.agroal.spi.JdbcDataSourceSchemaReadyBuildItem; import io.quarkus.agroal.spi.JdbcInitialSQLGeneratorBuildItem; @@ -210,7 +211,8 @@ void createBeans(FlywayRecorder recorder, .setRuntimeInit() .unremovable() .addInjectionPoint(ClassType.create(DotName.createSimple(FlywayContainerProducer.class))) - .addInjectionPoint(ClassType.create(DotName.createSimple(DataSources.class))) + .addInjectionPoint(ClassType.create(DotName.createSimple(AgroalDataSource.class)), + AgroalDataSourceBuildUtil.qualifier(dataSourceName)) .createWith(recorder.flywayContainerFunction(dataSourceName, hasMigrations, createPossible)); AnnotationInstance flywayContainerQualifier; diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainerUtil.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainerUtil.java index 34e6a4629f295d..d041506c7e5cc4 100644 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainerUtil.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainerUtil.java @@ -6,7 +6,7 @@ import jakarta.enterprise.inject.Default; -import io.quarkus.agroal.runtime.DataSources; +import io.quarkus.agroal.runtime.AgroalDataSourceUtil; import io.quarkus.arc.Arc; import io.quarkus.arc.InstanceHandle; import io.quarkus.datasource.common.runtime.DataSourceUtil; @@ -23,7 +23,7 @@ public static FlywayContainer getFlywayContainer(String dataSourceName) { public static List getActiveFlywayContainers() { List result = new ArrayList<>(); - for (String datasourceName : Arc.container().instance(DataSources.class).get().getActiveDataSourceNames()) { + for (String datasourceName : AgroalDataSourceUtil.activeDataSourceNames()) { InstanceHandle handle = Arc.container().instance(FlywayContainer.class, getFlywayContainerQualifier(datasourceName)); if (!handle.isAvailable()) { 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 3af211f6828917..0823927f3c0d0d 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 @@ -20,9 +20,11 @@ import org.flywaydb.core.internal.schemahistory.SchemaHistory; import org.jboss.logging.Logger; -import io.quarkus.agroal.runtime.DataSources; +import io.agroal.api.AgroalDataSource; +import io.quarkus.agroal.runtime.AgroalDataSourceUtil; import io.quarkus.agroal.runtime.UnconfiguredDataSource; import io.quarkus.arc.Arc; +import io.quarkus.arc.InactiveBeanException; import io.quarkus.arc.InstanceHandle; import io.quarkus.arc.SyntheticCreationalContext; import io.quarkus.datasource.common.runtime.DataSourceUtil; @@ -64,14 +66,14 @@ public Function, FlywayContainer> fl public FlywayContainer apply(SyntheticCreationalContext context) { DataSource dataSource; try { - dataSource = context.getInjectedReference(DataSources.class).getDataSource(dataSourceName); + dataSource = context.getInjectedReference(AgroalDataSource.class, + AgroalDataSourceUtil.qualifier(dataSourceName)); if (dataSource instanceof UnconfiguredDataSource) { throw DataSourceUtil.dataSourceNotConfigured(dataSourceName); } - } catch (ConfigurationException e) { + } catch (ConfigurationException | InactiveBeanException e) { // TODO do we really want to enable retrieval of a FlywayContainer for an unconfigured/inactive datasource? - // Assigning ApplicationScoped to the FlywayContainer - // and throwing UnsatisfiedResolutionException on bean creation (first access) + // Marking the FlywayContainer bean as inactive when the datasource is inactive/unconfigured // would probably make more sense. return new UnconfiguredDataSourceFlywayContainer(dataSourceName, String.format(Locale.ROOT, "Unable to find datasource '%s' for Flyway: %s", @@ -101,8 +103,7 @@ public void doStartActions(String dataSourceName) { if (!flywayDataSourceRuntimeConfig.active // If not specified explicitly, Flyway is active when the datasource itself is active. - .orElseGet(() -> Arc.container().instance(DataSources.class).get().getActiveDataSourceNames() - .contains(dataSourceName))) { + .orElseGet(() -> AgroalDataSourceUtil.dataSourceIfActive(dataSourceName).isPresent())) { return; } diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java index 7a17d32e38408e..fbc3f973e89539 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java @@ -27,7 +27,7 @@ import org.jboss.jandex.ParameterizedType; import org.jboss.jandex.Type; -import io.quarkus.agroal.runtime.DataSources; +import io.agroal.api.AgroalDataSource; import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem; @@ -155,7 +155,7 @@ void generateJpaConfigBean(HibernateOrmRecorder recorder, AnnotationInstance.builder(Any.class).build()); } else { configurator.addInjectionPoint(ParameterizedType.create(DotName.createSimple(Instance.class), - new Type[] { ClassType.create(DotName.createSimple(DataSources.class)) }, null), + new Type[] { ClassType.create(DotName.createSimple(AgroalDataSource.class)) }, null), AnnotationInstance.builder(Any.class).build()); } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java index c87cc25b89e960..c4cd69396e830d 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java @@ -24,7 +24,7 @@ import org.hibernate.service.internal.ProvidedService; import org.jboss.logging.Logger; -import io.quarkus.agroal.runtime.DataSources; +import io.quarkus.agroal.runtime.AgroalDataSourceUtil; import io.quarkus.agroal.runtime.UnconfiguredDataSource; import io.quarkus.arc.Arc; import io.quarkus.datasource.common.runtime.DataSourceUtil; @@ -386,7 +386,7 @@ private static void injectDataSource(String persistenceUnitName, String dataSour DataSource dataSource; try { - dataSource = Arc.container().instance(DataSources.class).get().getDataSource(dataSourceName); + dataSource = AgroalDataSourceUtil.dataSourceInstance(dataSourceName).get(); if (dataSource instanceof UnconfiguredDataSource) { throw DataSourceUtil.dataSourceNotConfigured(dataSourceName); } diff --git a/extensions/liquibase/deployment/src/main/java/io/quarkus/liquibase/deployment/LiquibaseProcessor.java b/extensions/liquibase/deployment/src/main/java/io/quarkus/liquibase/deployment/LiquibaseProcessor.java index 8b415d41106e4b..9a631e0387222a 100644 --- a/extensions/liquibase/deployment/src/main/java/io/quarkus/liquibase/deployment/LiquibaseProcessor.java +++ b/extensions/liquibase/deployment/src/main/java/io/quarkus/liquibase/deployment/LiquibaseProcessor.java @@ -30,7 +30,8 @@ import org.jboss.jandex.DotName; import org.jboss.logging.Logger; -import io.quarkus.agroal.runtime.DataSources; +import io.agroal.api.AgroalDataSource; +import io.quarkus.agroal.deployment.AgroalDataSourceBuildUtil; import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; import io.quarkus.agroal.spi.JdbcDataSourceSchemaReadyBuildItem; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; @@ -301,7 +302,9 @@ void createBeans(LiquibaseRecorder recorder, .setRuntimeInit() .unremovable() .addInjectionPoint(ClassType.create(DotName.createSimple(LiquibaseFactoryProducer.class))) - .addInjectionPoint(ClassType.create(DotName.createSimple(DataSources.class))) + .addInjectionPoint(ClassType.create(DotName.createSimple(AgroalDataSource.class)), + AgroalDataSourceBuildUtil.qualifier(dataSourceName)) + .checkActive(recorder.liquibaseActiveChecker(dataSourceName)) .createWith(recorder.liquibaseFunction(dataSourceName)); if (DataSourceUtil.isDefault(dataSourceName)) { diff --git a/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseFactoryUtil.java b/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseFactoryUtil.java index c3d4698cfe4c6f..975600c6aac606 100644 --- a/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseFactoryUtil.java +++ b/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseFactoryUtil.java @@ -6,7 +6,7 @@ import jakarta.enterprise.inject.Default; -import io.quarkus.agroal.runtime.DataSources; +import io.quarkus.agroal.runtime.AgroalDataSourceUtil; import io.quarkus.arc.Arc; import io.quarkus.arc.InstanceHandle; import io.quarkus.datasource.common.runtime.DataSourceUtil; @@ -24,7 +24,7 @@ public static InstanceHandle getLiquibaseFactory(String dataSo public static List> getActiveLiquibaseFactories() { List> result = new ArrayList<>(); - for (String datasourceName : Arc.container().instance(DataSources.class).get().getActiveDataSourceNames()) { + for (String datasourceName : AgroalDataSourceUtil.activeDataSourceNames()) { InstanceHandle handle = Arc.container().instance(LiquibaseFactory.class, getLiquibaseFactoryQualifier(datasourceName)); if (!handle.isAvailable()) { diff --git a/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseRecorder.java b/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseRecorder.java index 01bde1120a5776..6696a9083c19e3 100644 --- a/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseRecorder.java +++ b/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseRecorder.java @@ -7,9 +7,8 @@ import jakarta.enterprise.inject.UnsatisfiedResolutionException; -import io.quarkus.agroal.runtime.DataSources; +import io.quarkus.agroal.runtime.AgroalDataSourceUtil; import io.quarkus.agroal.runtime.UnconfiguredDataSource; -import io.quarkus.arc.Arc; import io.quarkus.arc.InstanceHandle; import io.quarkus.arc.SyntheticCreationalContext; import io.quarkus.datasource.common.runtime.DataSourceUtil; @@ -29,13 +28,13 @@ public LiquibaseRecorder(RuntimeValue config) { this.config = config; } - public Function, LiquibaseFactory> liquibaseFunction(String dataSourceName) { - return new Function, LiquibaseFactory>() { + public Runnable liquibaseActiveChecker(String dataSourceName) { + // Liquibase is active when the datasource itself is active. + return new Runnable() { @Override - public LiquibaseFactory apply(SyntheticCreationalContext context) { - DataSource dataSource; + public void run() { try { - dataSource = context.getInjectedReference(DataSources.class).getDataSource(dataSourceName); + var dataSource = AgroalDataSourceUtil.dataSourceInstance(dataSourceName).get(); if (dataSource instanceof UnconfiguredDataSource) { throw DataSourceUtil.dataSourceNotConfigured(dataSourceName); } @@ -44,8 +43,17 @@ public LiquibaseFactory apply(SyntheticCreationalContext conte "Unable to find datasource '%s' for Liquibase: %s", dataSourceName, e.getMessage()), e); } + } + }; + } + public Function, LiquibaseFactory> liquibaseFunction(String dataSourceName) { + return new Function, LiquibaseFactory>() { + @Override + public LiquibaseFactory apply(SyntheticCreationalContext context) { LiquibaseFactoryProducer liquibaseProducer = context.getInjectedReference(LiquibaseFactoryProducer.class); + DataSource dataSource = context.getInjectedReference(DataSource.class, + AgroalDataSourceUtil.qualifier(dataSourceName)); return liquibaseProducer.createLiquibaseFactory(dataSource, dataSourceName); } }; @@ -55,10 +63,6 @@ public void doStartActions(String dataSourceName) { if (!config.getValue().enabled) { return; } - // Liquibase is active when the datasource itself is active. - if (!Arc.container().instance(DataSources.class).get().getActiveDataSourceNames().contains(dataSourceName)) { - return; - } InstanceHandle liquibaseFactoryHandle = LiquibaseFactoryUtil.getLiquibaseFactory(dataSourceName); try { @@ -87,8 +91,6 @@ public void doStartActions(String dataSourceName) { } } } - } catch (UnsatisfiedResolutionException e) { - //ignore, the DS is not configured } catch (Exception e) { throw new IllegalStateException("Error starting Liquibase", e); }