From 9705caec23445e492fbbd273ca7032ff0394e2c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Thu, 7 Dec 2023 14:00:46 +0100 Subject: [PATCH] Add runtime configuration property quarkus.datasource.active --- .../agroal/deployment/AgroalProcessor.java | 17 ++-- .../AgroalMetricsConfigActiveFalseTest.java | 51 ++++++++++ ...onfigActiveFalseDefaultDatasourceTest.java | 91 ++++++++++++++++++ .../ConfigActiveFalseNamedDatasourceTest.java | 96 +++++++++++++++++++ .../DataSourceHealthCheckConfigFalseTest.java | 39 ++++++++ ...port.java => AgroalDataSourceSupport.java} | 4 +- .../agroal/runtime/AgroalRecorder.java | 8 +- .../quarkus/agroal/runtime/DataSources.java | 43 +++++++-- .../runtime/health/DataSourceHealthCheck.java | 28 +++--- .../metrics/AgroalMetricsRecorder.java | 26 ++--- .../common/runtime/DataSourceUtil.java | 10 ++ ...rcesExcludedFromHealthChecksProcessor.java | 27 +++--- .../DevServicesDatasourceProcessor.java | 12 ++- .../runtime/DataSourceRecorder.java | 44 +++++++++ .../runtime/DataSourceRuntimeConfig.java | 14 +++ .../datasource/runtime/DataSourceSupport.java | 42 ++++++++ .../runtime/DataSourcesHealthSupport.java | 26 ----- .../DataSourcesHealthSupportRecorder.java | 31 ------ ...onfigActiveFalseDefaultDatasourceTest.java | 38 ++++++++ ...nConfigActiveFalseNamedDataSourceTest.java | 49 ++++++++++ ...efaultDatasourceConfigActiveFalseTest.java | 42 ++++++++ ...tNamedDatasourceConfigActiveFalseTest.java | 52 ++++++++++ .../flyway/runtime/FlywayContainerUtil.java | 44 +++++++++ .../runtime/FlywayContainersSupplier.java | 13 +-- .../FlywayDataSourceRuntimeConfig.java | 4 +- .../flyway/runtime/FlywayRecorder.java | 23 ++--- .../flyway/runtime/FlywaySchemaProvider.java | 12 +-- ...plicitDatasourceConfigActiveFalseTest.java | 41 ++++++++ ...plicitDatasourceConfigActiveFalseTest.java | 36 +++++++ ...plicitDatasourceConfigActiveFalseTest.java | 41 ++++++++ .../deployment/LiquibaseProcessor.java | 7 +- ...onfigActiveFalseDefaultDatasourceTest.java | 39 ++++++++ ...nConfigActiveFalseNamedDatasourceTest.java | 48 ++++++++++ ...efaultDatasourceConfigActiveFalseTest.java | 42 ++++++++ ...tNamedDatasourceConfigActiveFalseTest.java | 52 ++++++++++ .../runtime/LiquibaseFactoryUtil.java | 45 +++++++++ .../liquibase/runtime/LiquibaseRecorder.java | 58 +++++------ .../runtime/LiquibaseSchemaProvider.java | 29 ++---- .../devui/LiquibaseFactoriesSupplier.java | 20 +--- .../ReactiveDB2ClientProcessor.java | 3 + .../db2/client/runtime/DB2PoolRecorder.java | 4 + .../ReactiveDB2DataSourcesHealthCheck.java | 6 +- .../ReactiveMSSQLClientProcessor.java | 3 + .../client/runtime/MSSQLPoolRecorder.java | 4 + .../ReactiveMSSQLDataSourcesHealthCheck.java | 6 +- .../ReactiveMySQLClientProcessor.java | 3 + .../client/runtime/MySQLPoolRecorder.java | 4 + .../ReactiveMySQLDataSourcesHealthCheck.java | 6 +- .../ReactiveOracleClientProcessor.java | 3 + .../client/runtime/OraclePoolRecorder.java | 4 + .../ReactiveOracleDataSourcesHealthCheck.java | 6 +- .../deployment/ReactivePgClientProcessor.java | 3 + .../pg/client/runtime/PgPoolRecorder.java | 4 + .../ReactivePgDataSourcesHealthCheck.java | 6 +- 54 files changed, 1163 insertions(+), 246 deletions(-) create mode 100644 extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/AgroalMetricsConfigActiveFalseTest.java create mode 100644 extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseDefaultDatasourceTest.java create mode 100644 extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseNamedDatasourceTest.java create mode 100644 extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/DataSourceHealthCheckConfigFalseTest.java rename extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/{DataSourceSupport.java => AgroalDataSourceSupport.java} (87%) create mode 100644 extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourceRecorder.java create mode 100644 extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourceSupport.java delete mode 100644 extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourcesHealthSupport.java delete mode 100644 extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourcesHealthSupportRecorder.java create mode 100644 extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigActiveFalseDefaultDatasourceTest.java create mode 100644 extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigActiveFalseNamedDataSourceTest.java create mode 100644 extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartDefaultDatasourceConfigActiveFalseTest.java create mode 100644 extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartNamedDatasourceConfigActiveFalseTest.java create mode 100644 extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainerUtil.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithExplicitDatasourceConfigActiveFalseTest.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitDatasourceConfigActiveFalseTest.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInNamedPUWithExplicitDatasourceConfigActiveFalseTest.java create mode 100644 extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigActiveFalseDefaultDatasourceTest.java create mode 100644 extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigActiveFalseNamedDatasourceTest.java create mode 100644 extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigActiveFalseTest.java create mode 100644 extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartNamedDatasourceConfigActiveFalseTest.java create mode 100644 extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseFactoryUtil.java 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 b52cb438b06dc..740b255164904 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 @@ -24,10 +24,10 @@ import io.agroal.api.AgroalDataSource; 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.DataSourceSupport; import io.quarkus.agroal.runtime.DataSources; import io.quarkus.agroal.runtime.DataSourcesJdbcBuildTimeConfig; import io.quarkus.agroal.runtime.JdbcDriver; @@ -202,20 +202,20 @@ private static void validateBuildTimeConfig(AggregatedDataSourceBuildTimeConfigB } } - private DataSourceSupport getDataSourceSupport( + private AgroalDataSourceSupport getDataSourceSupport( List aggregatedBuildTimeConfigBuildItems, SslNativeConfigBuildItem sslNativeConfig, Capabilities capabilities) { - Map dataSourceSupportEntries = new HashMap<>(); + Map dataSourceSupportEntries = new HashMap<>(); for (AggregatedDataSourceBuildTimeConfigBuildItem aggregatedDataSourceBuildTimeConfig : aggregatedBuildTimeConfigBuildItems) { String dataSourceName = aggregatedDataSourceBuildTimeConfig.getName(); dataSourceSupportEntries.put(dataSourceName, - new DataSourceSupport.Entry(dataSourceName, aggregatedDataSourceBuildTimeConfig.getDbKind(), + new AgroalDataSourceSupport.Entry(dataSourceName, aggregatedDataSourceBuildTimeConfig.getDbKind(), aggregatedDataSourceBuildTimeConfig.getDataSourceConfig().dbVersion(), aggregatedDataSourceBuildTimeConfig.getResolvedDriverClass(), aggregatedDataSourceBuildTimeConfig.isDefault())); } - return new DataSourceSupport(sslNativeConfig.isExplicitlyDisabled(), + return new AgroalDataSourceSupport(sslNativeConfig.isExplicitlyDisabled(), capabilities.isPresent(Capability.METRICS), dataSourceSupportEntries); } @@ -247,10 +247,11 @@ void generateDataSourceSupportBean(AgroalRecorder recorder, unremovableBeans.produce(UnremovableBeanBuildItem.beanTypes(AgroalPoolInterceptor.class)); // create the DataSourceSupport bean that DataSourceProducer uses as a dependency - DataSourceSupport dataSourceSupport = getDataSourceSupport(aggregatedBuildTimeConfigBuildItems, sslNativeConfig, + AgroalDataSourceSupport agroalDataSourceSupport = getDataSourceSupport(aggregatedBuildTimeConfigBuildItems, + sslNativeConfig, capabilities); - syntheticBeanBuildItemBuildProducer.produce(SyntheticBeanBuildItem.configure(DataSourceSupport.class) - .supplier(recorder.dataSourceSupportSupplier(dataSourceSupport)) + syntheticBeanBuildItemBuildProducer.produce(SyntheticBeanBuildItem.configure(AgroalDataSourceSupport.class) + .supplier(recorder.dataSourceSupportSupplier(agroalDataSourceSupport)) .unremovable() .done()); } diff --git a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/AgroalMetricsConfigActiveFalseTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/AgroalMetricsConfigActiveFalseTest.java new file mode 100644 index 0000000000000..b680914997d6a --- /dev/null +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/AgroalMetricsConfigActiveFalseTest.java @@ -0,0 +1,51 @@ +package io.quarkus.agroal.test; + +import jakarta.inject.Inject; + +import org.eclipse.microprofile.metrics.Counter; +import org.eclipse.microprofile.metrics.Gauge; +import org.eclipse.microprofile.metrics.MetricID; +import org.eclipse.microprofile.metrics.MetricRegistry; +import org.eclipse.microprofile.metrics.Tag; +import org.eclipse.microprofile.metrics.annotation.RegistryType; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class AgroalMetricsConfigActiveFalseTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withConfigurationResource("application-metrics-enabled.properties") + .overrideConfigKey("quarkus.datasource.active", "false") + .overrideConfigKey("quarkus.datasource.ds1.active", "false"); + + @Inject + @RegistryType(type = MetricRegistry.Type.VENDOR) + MetricRegistry registry; + + @Test + public void testMetricsOfDefaultDS() { + Counter acquireCount = registry.getCounters() + .get(new MetricID("agroal.acquire.count", new Tag("datasource", "default"))); + Gauge maxUsed = registry.getGauges() + .get(new MetricID("agroal.max.used.count", new Tag("datasource", "default"))); + + Assertions.assertNull(acquireCount, "Agroal metrics should not be registered for deactivated datasources eagerly"); + Assertions.assertNull(maxUsed, "Agroal metrics should not be registered for deactivated datasources eagerly"); + } + + @Test + public void testMetricsOfDs1() { + Counter acquireCount = registry.getCounters().get(new MetricID("agroal.acquire.count", + new Tag("datasource", "ds1"))); + Gauge maxUsed = registry.getGauges().get(new MetricID("agroal.max.used.count", + new Tag("datasource", "ds1"))); + + Assertions.assertNull(acquireCount, "Agroal metrics should not be registered for deactivated datasources eagerly"); + Assertions.assertNull(maxUsed, "Agroal metrics should not be registered for deactivated datasources eagerly"); + } + +} 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/ConfigActiveFalseDefaultDatasourceTest.java new file mode 100644 index 0000000000000..4e71a02d8503e --- /dev/null +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseDefaultDatasourceTest.java @@ -0,0 +1,91 @@ +package io.quarkus.agroal.test; + +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.test.QuarkusUnitTest; + +public class ConfigActiveFalseDefaultDatasourceTest { + + @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. + 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 agroalDataSource() { + AgroalDataSource ds = Arc.container().instance(AgroalDataSource.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. + 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) + .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/ConfigActiveFalseNamedDatasourceTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseNamedDatasourceTest.java new file mode 100644 index 0000000000000..812aeadd02514 --- /dev/null +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/ConfigActiveFalseNamedDatasourceTest.java @@ -0,0 +1,96 @@ +package io.quarkus.agroal.test; + +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.test.QuarkusUnitTest; + +public class ConfigActiveFalseNamedDatasourceTest { + + @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"); + + @Inject + MyBean myBean; + + @Test + public void dataSource() { + DataSource ds = Arc.container().instance(DataSource.class, + new io.quarkus.agroal.DataSource.DataSourceLiteral("users")).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. + 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 agroalDataSource() { + DataSource ds = Arc.container().instance(DataSource.class, + new io.quarkus.agroal.DataSource.DataSourceLiteral("users")).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. + 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) + .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/DataSourceHealthCheckConfigFalseTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/DataSourceHealthCheckConfigFalseTest.java new file mode 100644 index 0000000000000..3fecd302a9ae9 --- /dev/null +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/DataSourceHealthCheckConfigFalseTest.java @@ -0,0 +1,39 @@ +package io.quarkus.agroal.test; + +import org.hamcrest.CoreMatchers; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class DataSourceHealthCheckConfigFalseTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar.addAsResource(new StringAsset(""" + quarkus.datasource.db-kind=h2 + quarkus.datasource.username=username-default + quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/mem:default + quarkus.datasource.jdbc.max-size=13 + + # this data source is broken, but will be deactivated, + # so the overall check should pass + quarkus.datasource.brokenDS.db-kind=h2 + quarkus.datasource.brokenDS.jdbc.url=BROKEN + + quarkus.datasource.health.enabled=true + + quarkus.datasource.brokenDS.active=false + """), + "application.properties")); + + @Test + public void testDataSourceHealthCheckExclusion() { + RestAssured.when().get("/q/health/ready") + .then() + .body("status", CoreMatchers.equalTo("UP")); + } + +} diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceSupport.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalDataSourceSupport.java similarity index 87% rename from extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceSupport.java rename to extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalDataSourceSupport.java index 886085b97f797..fdf6f9c77e793 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceSupport.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/AgroalDataSourceSupport.java @@ -3,13 +3,13 @@ import java.util.Map; import java.util.Optional; -public class DataSourceSupport { +public class AgroalDataSourceSupport { public final boolean disableSslSupport; public final boolean mpMetricsPresent; public final Map entries; - public DataSourceSupport(boolean disableSslSupport, boolean mpMetricsPresent, Map entries) { + public AgroalDataSourceSupport(boolean disableSslSupport, boolean mpMetricsPresent, Map entries) { this.disableSslSupport = disableSslSupport; this.mpMetricsPresent = mpMetricsPresent; this.entries = entries; 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 a69ce8f788d32..52b7d1a20907d 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 @@ -11,11 +11,11 @@ @Recorder public class AgroalRecorder { - public Supplier dataSourceSupportSupplier(DataSourceSupport dataSourceSupport) { - return new Supplier() { + public Supplier dataSourceSupportSupplier(AgroalDataSourceSupport agroalDataSourceSupport) { + return new Supplier() { @Override - public DataSourceSupport get() { - return dataSourceSupport; + public AgroalDataSourceSupport get() { + return agroalDataSourceSupport; } }; } 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 0e6e827b06a42..3fc83fad2d675 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 @@ -8,6 +8,7 @@ import java.util.Iterator; import java.util.Map; import java.util.ServiceLoader; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; @@ -45,6 +46,7 @@ import io.quarkus.credentials.runtime.CredentialsProviderFinder; import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.datasource.runtime.DataSourceRuntimeConfig; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.datasource.runtime.DataSourcesBuildTimeConfig; import io.quarkus.datasource.runtime.DataSourcesRuntimeConfig; import io.quarkus.narayana.jta.runtime.TransactionManagerConfiguration; @@ -76,6 +78,7 @@ public class DataSources { private final XAResourceRecoveryRegistry xaResourceRecoveryRegistry; private final TransactionSynchronizationRegistry transactionSynchronizationRegistry; private final DataSourceSupport dataSourceSupport; + private final AgroalDataSourceSupport agroalDataSourceSupport; private final Instance agroalPoolInterceptors; private final Instance agroalOpenTelemetryWrapper; @@ -89,6 +92,7 @@ public DataSources(DataSourcesBuildTimeConfig dataSourcesBuildTimeConfig, XAResourceRecoveryRegistry xaResourceRecoveryRegistry, TransactionSynchronizationRegistry transactionSynchronizationRegistry, DataSourceSupport dataSourceSupport, + AgroalDataSourceSupport agroalDataSourceSupport, @Any Instance agroalPoolInterceptors, Instance agroalOpenTelemetryWrapper) { this.dataSourcesBuildTimeConfig = dataSourcesBuildTimeConfig; @@ -100,6 +104,7 @@ public DataSources(DataSourcesBuildTimeConfig dataSourcesBuildTimeConfig, this.xaResourceRecoveryRegistry = xaResourceRecoveryRegistry; this.transactionSynchronizationRegistry = transactionSynchronizationRegistry; this.dataSourceSupport = dataSourceSupport; + this.agroalDataSourceSupport = agroalDataSourceSupport; this.agroalPoolInterceptors = agroalPoolInterceptors; this.agroalOpenTelemetryWrapper = agroalOpenTelemetryWrapper; } @@ -127,25 +132,36 @@ public boolean isDataSourceCreated(String dataSourceName) { return dataSources.containsKey(dataSourceName); } + public Set getActiveDataSourceNames() { + // Datasources are created on startup, + // and we only create active datasources. + return dataSources.keySet(); + } + public AgroalDataSource getDataSource(String dataSourceName) { return dataSources.computeIfAbsent(dataSourceName, new Function() { @Override public AgroalDataSource apply(String s) { - return doCreateDataSource(s); + return doCreateDataSource(s, true); } }); } @PostConstruct public void start() { - for (String dataSourceName : dataSourceSupport.entries.keySet()) { - getDataSource(dataSourceName); + for (String dataSourceName : agroalDataSourceSupport.entries.keySet()) { + dataSources.computeIfAbsent(dataSourceName, new Function() { + @Override + public AgroalDataSource apply(String s) { + return doCreateDataSource(s, false); + } + }); } } @SuppressWarnings("resource") - public AgroalDataSource doCreateDataSource(String dataSourceName) { - if (!dataSourceSupport.entries.containsKey(dataSourceName)) { + public AgroalDataSource doCreateDataSource(String dataSourceName, boolean failIfInactive) { + if (!agroalDataSourceSupport.entries.containsKey(dataSourceName)) { throw new IllegalArgumentException("No datasource named '" + dataSourceName + "' exists"); } @@ -153,10 +169,18 @@ public AgroalDataSource doCreateDataSource(String dataSourceName) { .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 .getDataSourceJdbcRuntimeConfig(dataSourceName); - - DataSourceSupport.Entry matchingSupportEntry = dataSourceSupport.entries.get(dataSourceName); if (!dataSourceJdbcRuntimeConfig.url().isPresent()) { //this is not an error situation, because we want to allow the situation where a JDBC extension //is installed but has not been configured @@ -167,6 +191,7 @@ public AgroalDataSource doCreateDataSource(String dataSourceName) { // we first make sure that all available JDBC drivers are loaded in the current TCCL loadDriversInTCCL(); + AgroalDataSourceSupport.Entry matchingSupportEntry = agroalDataSourceSupport.entries.get(dataSourceName); String resolvedDriverClass = matchingSupportEntry.resolvedDriverClass; Class driver; try { @@ -233,13 +258,13 @@ public AgroalDataSource doCreateDataSource(String dataSourceName) { AgroalConnectionFactoryConfigurationSupplier connectionFactoryConfiguration = poolConfiguration .connectionFactoryConfiguration(); - boolean mpMetricsPresent = dataSourceSupport.mpMetricsPresent; + boolean mpMetricsPresent = agroalDataSourceSupport.mpMetricsPresent; applyNewConfiguration(dataSourceName, dataSourceConfiguration, poolConfiguration, connectionFactoryConfiguration, driver, jdbcUrl, dataSourceJdbcBuildTimeConfig, dataSourceRuntimeConfig, dataSourceJdbcRuntimeConfig, transactionRuntimeConfig, mpMetricsPresent); - if (dataSourceSupport.disableSslSupport) { + if (agroalDataSourceSupport.disableSslSupport) { agroalConnectionConfigurer.disableSslSupport(resolvedDbKind, dataSourceConfiguration); } //we use a custom cache for two reasons: 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 36f7fd7d7a2fd..bee625afc55d2 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 @@ -9,6 +9,7 @@ import jakarta.annotation.PostConstruct; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; import org.eclipse.microprofile.health.HealthCheck; import org.eclipse.microprofile.health.HealthCheckResponse; @@ -16,28 +17,33 @@ import org.eclipse.microprofile.health.Readiness; import io.agroal.api.AgroalDataSource; -import io.quarkus.agroal.DataSource.DataSourceLiteral; +import io.quarkus.agroal.runtime.DataSources; import io.quarkus.arc.Arc; import io.quarkus.datasource.common.runtime.DataSourceUtil; -import io.quarkus.datasource.runtime.DataSourcesHealthSupport; +import io.quarkus.datasource.runtime.DataSourceSupport; @Readiness @ApplicationScoped public class DataSourceHealthCheck implements HealthCheck { - private final Map dataSources = new HashMap<>(); + + @Inject + DataSources dataSources; + + private final Map checkedDataSources = new HashMap<>(); @PostConstruct protected void init() { - DataSourcesHealthSupport support = Arc.container().instance(DataSourcesHealthSupport.class) + DataSourceSupport support = Arc.container().instance(DataSourceSupport.class) .get(); Set names = support.getConfiguredNames(); - Set excludedNames = support.getExcludedNames(); + Set excludedNames = support.getInactiveOrHealthCheckExcludedNames(); for (String name : names) { - DataSource ds = DataSourceUtil.isDefault(name) - ? (DataSource) Arc.container().instance(DataSource.class).get() - : (DataSource) Arc.container().instance(DataSource.class, new DataSourceLiteral(name)).get(); - if (!excludedNames.contains(name) && ds != null) { - dataSources.put(name, ds); + if (excludedNames.contains(name)) { + continue; + } + DataSource ds = dataSources.getDataSource(name); + if (ds != null) { + checkedDataSources.put(name, ds); } } } @@ -45,7 +51,7 @@ protected void init() { @Override public HealthCheckResponse call() { HealthCheckResponseBuilder builder = HealthCheckResponse.named("Database connections health check").up(); - for (Map.Entry dataSource : dataSources.entrySet()) { + for (Map.Entry dataSource : checkedDataSources.entrySet()) { boolean isDefault = DataSourceUtil.isDefault(dataSource.getKey()); AgroalDataSource ads = (AgroalDataSource) dataSource.getValue(); String dsName = dataSource.getKey(); 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 081ef0c0b6d95..726a0c0512e5a 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 @@ -5,9 +5,10 @@ import java.util.function.Function; import java.util.function.Supplier; -import io.agroal.api.AgroalDataSource; +import org.jboss.logging.Logger; + import io.agroal.api.AgroalDataSourceMetrics; -import io.quarkus.agroal.DataSource; +import io.quarkus.agroal.runtime.DataSources; import io.quarkus.arc.Arc; import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.runtime.annotations.Recorder; @@ -19,6 +20,8 @@ */ @Recorder public class AgroalMetricsRecorder { + private static final Logger log = Logger.getLogger(AgroalMetricsRecorder.class); + static Function, Long> convertToMillis = new Function, Long>() { @Override public Long apply(Supplier durationSupplier) { @@ -31,8 +34,15 @@ 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"); + return; + } + String tagValue = DataSourceUtil.isDefault(dataSourceName) ? "default" : dataSourceName; - AgroalDataSourceMetrics metrics = getDataSource(dataSourceName).getMetrics(); + AgroalDataSourceMetrics metrics = dataSources.getDataSource(dataSourceName).getMetrics(); metricsFactory.builder("agroal.active.count") .description( @@ -114,14 +124,4 @@ public void accept(MetricsFactory metricsFactory) { } }; } - - private AgroalDataSource getDataSource(String dataSourceName) { - if (dataSourceName == null || DataSourceUtil.isDefault(dataSourceName)) { - return Arc.container().instance(AgroalDataSource.class).get(); - } else { - return Arc.container() - .instance(AgroalDataSource.class, new DataSource.DataSourceLiteral(dataSourceName)) - .get(); - } - } } diff --git a/extensions/datasource/common/src/main/java/io/quarkus/datasource/common/runtime/DataSourceUtil.java b/extensions/datasource/common/src/main/java/io/quarkus/datasource/common/runtime/DataSourceUtil.java index 7b11b9e4aab7a..8158ad25b7e3c 100644 --- a/extensions/datasource/common/src/main/java/io/quarkus/datasource/common/runtime/DataSourceUtil.java +++ b/extensions/datasource/common/src/main/java/io/quarkus/datasource/common/runtime/DataSourceUtil.java @@ -50,6 +50,16 @@ public static ConfigurationException dataSourceNotConfigured(String dataSourceNa dataSourcePropertyKey(dataSourceName, "jdbc.url"))); } + public static ConfigurationException dataSourceInactive(String dataSourceName) { + return new ConfigurationException(String.format(Locale.ROOT, + "Datasource '%s' 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 '%s' to 'true' and configure datasource '%s'." + + " Refer to https://quarkus.io/guides/datasource for guidance.", + dataSourceName, dataSourcePropertyKey(dataSourceName, "active"), dataSourceName), + Set.of(dataSourcePropertyKey(dataSourceName, "active"))); + } + private DataSourceUtil() { } diff --git a/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/DataSourcesExcludedFromHealthChecksProcessor.java b/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/DataSourcesExcludedFromHealthChecksProcessor.java index 80b43e8033719..22f45aefa2ed9 100644 --- a/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/DataSourcesExcludedFromHealthChecksProcessor.java +++ b/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/DataSourcesExcludedFromHealthChecksProcessor.java @@ -1,15 +1,15 @@ package io.quarkus.datasource.deployment; -import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; +import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT; import jakarta.inject.Singleton; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; +import io.quarkus.datasource.runtime.DataSourceRecorder; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.datasource.runtime.DataSourcesBuildTimeConfig; -import io.quarkus.datasource.runtime.DataSourcesHealthSupport; -import io.quarkus.datasource.runtime.DataSourcesHealthSupportRecorder; +import io.quarkus.datasource.runtime.DataSourcesRuntimeConfig; import io.quarkus.deployment.Capabilities; -import io.quarkus.deployment.Capability; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.Record; @@ -17,18 +17,17 @@ public class DataSourcesExcludedFromHealthChecksProcessor { @BuildStep - @Record(STATIC_INIT) + @Record(RUNTIME_INIT) void produceBean( Capabilities capabilities, - DataSourcesHealthSupportRecorder recorder, - DataSourcesBuildTimeConfig config, + DataSourceRecorder recorder, + DataSourcesBuildTimeConfig buildTimeConfig, DataSourcesRuntimeConfig runtimeConfig, BuildProducer syntheticBeans) { - if (capabilities.isPresent(Capability.SMALLRYE_HEALTH)) { - syntheticBeans.produce(SyntheticBeanBuildItem.configure(DataSourcesHealthSupport.class) - .scope(Singleton.class) - .unremovable() - .runtimeValue(recorder.configureDataSourcesHealthSupport(config)) - .done()); - } + syntheticBeans.produce(SyntheticBeanBuildItem.configure(DataSourceSupport.class) + .scope(Singleton.class) + .unremovable() + .runtimeValue(recorder.createDataSourceSupport(buildTimeConfig, runtimeConfig)) + .setRuntimeInit() + .done()); } } diff --git a/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/devservices/DevServicesDatasourceProcessor.java b/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/devservices/DevServicesDatasourceProcessor.java index 22b6650110044..559fcbe6fa3d0 100644 --- a/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/devservices/DevServicesDatasourceProcessor.java +++ b/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/devservices/DevServicesDatasourceProcessor.java @@ -198,11 +198,17 @@ private RunningDevService startDevDb( DockerStatusBuildItem dockerStatusBuildItem, LaunchMode launchMode, Optional consoleInstalledBuildItem, LoggingSetupBuildItem loggingSetupBuildItem, GlobalDevServicesConfig globalDevServicesConfig) { - boolean explicitlyDisabled = !(dataSourceBuildTimeConfig.devservices().enabled().orElse(true)); String dataSourcePrettyName = DataSourceUtil.isDefault(dbName) ? "default datasource" : "datasource " + dbName; - if (explicitlyDisabled) { - //explicitly disabled + if (!ConfigUtils.getFirstOptionalValue( + DataSourceUtil.dataSourcePropertyKeys(dbName, "active"), Boolean.class) + .orElse(true)) { + log.debug("Not starting Dev Services for " + dataSourcePrettyName + + " as the datasource has been deactivated in the configuration"); + return null; + } + + if (!(dataSourceBuildTimeConfig.devservices().enabled().orElse(true))) { log.debug("Not starting Dev Services for " + dataSourcePrettyName + " as it has been disabled in the configuration"); return null; diff --git a/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourceRecorder.java b/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourceRecorder.java new file mode 100644 index 0000000000000..87a39c554f6c3 --- /dev/null +++ b/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourceRecorder.java @@ -0,0 +1,44 @@ +package io.quarkus.datasource.runtime; + +import static java.util.stream.Collectors.toUnmodifiableSet; + +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.annotations.Recorder; + +@Recorder +public class DataSourceRecorder { + + public RuntimeValue createDataSourceSupport( + DataSourcesBuildTimeConfig buildTimeConfig, + DataSourcesRuntimeConfig runtimeConfig) { + Stream.Builder configured = Stream.builder(); + Stream.Builder excludedForHealthChecks = Stream.builder(); + for (Map.Entry dataSource : buildTimeConfig.dataSources().entrySet()) { + // TODO this is wrong, as the default datasource could be configured without db-kind being set: + // it's inferred automatically for the default datasource when possible. + // See https://github.com/quarkusio/quarkus/issues/37779 + if (dataSource.getValue().dbKind().isPresent()) { + configured.add(dataSource.getKey()); + } + if (dataSource.getValue().healthExclude()) { + excludedForHealthChecks.add(dataSource.getKey()); + } + } + Set names = configured.build().collect(toUnmodifiableSet()); + Set excludedNames = excludedForHealthChecks.build().collect(toUnmodifiableSet()); + + Stream.Builder inactive = Stream.builder(); + for (Map.Entry entry : runtimeConfig.dataSources().entrySet()) { + if (!entry.getValue().active()) { + inactive.add(entry.getKey()); + } + } + Set inactiveNames = inactive.build().collect(toUnmodifiableSet()); + + return new RuntimeValue<>(new DataSourceSupport(names, excludedNames, inactiveNames)); + } +} diff --git a/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourceRuntimeConfig.java b/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourceRuntimeConfig.java index dc9cd2b972e99..05d101d40a942 100644 --- a/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourceRuntimeConfig.java +++ b/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourceRuntimeConfig.java @@ -5,10 +5,24 @@ import io.quarkus.runtime.annotations.ConfigGroup; import io.quarkus.runtime.configuration.TrimmedStringConverter; import io.smallrye.config.WithConverter; +import io.smallrye.config.WithDefault; @ConfigGroup public interface DataSourceRuntimeConfig { + /** + * Whether this datasource should be active at runtime. + * + * If the datasource is not active, it won't start with the application, + * and accessing the corresponding Datasource CDI bean will fail, + * meaning in particular that consumers of this datasource + * (e.g. Hibernate ORM persistence units) will fail to start unless they are inactive too. + * + * @asciidoclet + */ + @WithDefault("true") + boolean active(); + /** * The datasource username */ 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 new file mode 100644 index 0000000000000..96b4b0f1fa9a9 --- /dev/null +++ b/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourceSupport.java @@ -0,0 +1,42 @@ +package io.quarkus.datasource.runtime; + +import java.util.HashSet; +import java.util.Set; + +/** + * Helper class that holds the names of all configured data sources, + * along with the names of those that are inactive or excluded from health checks. + *

+ * This is used by any feature that needs runtime access to data sources, + * e.g. Flyway/Liquibase or health check implementation classes. + */ +public class DataSourceSupport { + + private final Set configuredNames; + private final Set inactiveNames; + private final Set inactiveOrHealthCheckExcludedNames; + + public DataSourceSupport(Set configuredNames, Set healthCheckExcludedNames, + Set inactiveNames) { + this.configuredNames = configuredNames; + this.inactiveOrHealthCheckExcludedNames = new HashSet<>(); + inactiveOrHealthCheckExcludedNames.addAll(inactiveNames); + inactiveOrHealthCheckExcludedNames.addAll(healthCheckExcludedNames); + this.inactiveNames = inactiveNames; + } + + // TODO careful when using this, as it might (incorrectly) not include the default datasource. + // See TODO in code that calls the constructor of this class. + // See https://github.com/quarkusio/quarkus/issues/37779 + public Set getConfiguredNames() { + return configuredNames; + } + + public Set getInactiveNames() { + return inactiveNames; + } + + public Set getInactiveOrHealthCheckExcludedNames() { + return inactiveOrHealthCheckExcludedNames; + } +} diff --git a/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourcesHealthSupport.java b/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourcesHealthSupport.java deleted file mode 100644 index 00a3c19a91b90..0000000000000 --- a/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourcesHealthSupport.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.quarkus.datasource.runtime; - -import java.util.Set; - -/** - * Helper class that holds the names of all configured data sources, along with the names of those - * that are excluded from health checks. This is used by health check implementation classes. - */ -public class DataSourcesHealthSupport { - - private final Set configuredNames; - private final Set excludedNames; - - public DataSourcesHealthSupport(Set configuredNames, Set excludedNames) { - this.configuredNames = configuredNames; - this.excludedNames = excludedNames; - } - - public Set getConfiguredNames() { - return configuredNames; - } - - public Set getExcludedNames() { - return excludedNames; - } -} diff --git a/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourcesHealthSupportRecorder.java b/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourcesHealthSupportRecorder.java deleted file mode 100644 index e39d700a6ed64..0000000000000 --- a/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DataSourcesHealthSupportRecorder.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.quarkus.datasource.runtime; - -import static java.util.stream.Collectors.toUnmodifiableSet; - -import java.util.Map; -import java.util.Set; -import java.util.stream.Stream; - -import io.quarkus.runtime.RuntimeValue; -import io.quarkus.runtime.annotations.Recorder; - -@Recorder -public class DataSourcesHealthSupportRecorder { - - public RuntimeValue configureDataSourcesHealthSupport( - DataSourcesBuildTimeConfig config) { - Stream.Builder configured = Stream.builder(); - Stream.Builder excluded = Stream.builder(); - for (Map.Entry dataSource : config.dataSources().entrySet()) { - if (dataSource.getValue().dbKind().isPresent()) { - configured.add(dataSource.getKey()); - } - if (dataSource.getValue().healthExclude()) { - excluded.add(dataSource.getKey()); - } - } - Set names = configured.build().collect(toUnmodifiableSet()); - Set excludedNames = excluded.build().collect(toUnmodifiableSet()); - return new RuntimeValue<>(new DataSourcesHealthSupport(names, excludedNames)); - } -} diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigActiveFalseDefaultDatasourceTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigActiveFalseDefaultDatasourceTest.java new file mode 100644 index 0000000000000..79ff4aad04ff4 --- /dev/null +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigActiveFalseDefaultDatasourceTest.java @@ -0,0 +1,38 @@ +package io.quarkus.flyway.test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.enterprise.inject.CreationException; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +import org.flywaydb.core.Flyway; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class FlywayExtensionConfigActiveFalseDefaultDatasourceTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .overrideConfigKey("quarkus.datasource.active", "false"); + + @Inject + Instance flywayForDefaultDatasource; + + @Test + @DisplayName("If the default datasource is deactivated, the application should boot, but Flyway should be deactivated for that datasource") + public void testBootSucceedsButFlywayDeactivated() { + assertThatThrownBy(flywayForDefaultDatasource::get) + .isInstanceOf(CreationException.class) + .cause() + .hasMessageContainingAll("Unable to find datasource '' for Flyway", + "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."); + } +} diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigActiveFalseNamedDataSourceTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigActiveFalseNamedDataSourceTest.java new file mode 100644 index 0000000000000..a00dd3f237b13 --- /dev/null +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigActiveFalseNamedDataSourceTest.java @@ -0,0 +1,49 @@ +package io.quarkus.flyway.test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.enterprise.inject.CreationException; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +import org.flywaydb.core.Flyway; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.flyway.FlywayDataSource; +import io.quarkus.test.QuarkusUnitTest; + +public class FlywayExtensionConfigActiveFalseNamedDataSourceTest { + + @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") + // We need this otherwise the *default* datasource may impact this test + .overrideConfigKey("quarkus.datasource.db-kind", "h2") + .overrideConfigKey("quarkus.datasource.username", "sa") + .overrideConfigKey("quarkus.datasource.password", "sa") + .overrideConfigKey("quarkus.datasource.jdbc.url", + "jdbc:h2:tcp://localhost/mem:test-quarkus-migrate-at-start;DB_CLOSE_DELAY=-1"); + + @Inject + @FlywayDataSource("users") + Instance flywayForNamedDatasource; + + @Test + @DisplayName("If a named datasource is deactivated, the application should boot, but Flyway should be deactivated for that datasource") + public void testBootSucceedsButFlywayDeactivated() { + assertThatThrownBy(flywayForNamedDatasource::get) + .isInstanceOf(CreationException.class) + .cause() + .hasMessageContainingAll("Unable to find datasource 'users' for Flyway", + "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."); + } +} diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartDefaultDatasourceConfigActiveFalseTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartDefaultDatasourceConfigActiveFalseTest.java new file mode 100644 index 0000000000000..240237ff5d727 --- /dev/null +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartDefaultDatasourceConfigActiveFalseTest.java @@ -0,0 +1,42 @@ +package io.quarkus.flyway.test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.enterprise.inject.CreationException; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +import org.flywaydb.core.Flyway; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class FlywayExtensionMigrateAtStartDefaultDatasourceConfigActiveFalseTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource("db/migration/V1.0.0__Quarkus.sql")) + .overrideConfigKey("quarkus.datasource.active", "false") + .overrideConfigKey("quarkus.flyway.migrate-at-start", "true"); + + @Inject + Instance flywayForDefaultDatasource; + + @Test + @DisplayName("If the default datasource is deactivated, even if migrate-at-start is enabled, the application should boot, but Flyway should be deactivated for that datasource") + public void testBootSucceedsButFlywayDeactivated() { + assertThatThrownBy(flywayForDefaultDatasource::get) + .isInstanceOf(CreationException.class) + .cause() + .hasMessageContainingAll("Unable to find datasource '' for Flyway", + "Datasource '' was deactivated through configuration properties.", + "To solve this, avoid accessing this datasource at runtime", + "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."); + } + +} diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartNamedDatasourceConfigActiveFalseTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartNamedDatasourceConfigActiveFalseTest.java new file mode 100644 index 0000000000000..7c2e303c9d4ce --- /dev/null +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartNamedDatasourceConfigActiveFalseTest.java @@ -0,0 +1,52 @@ +package io.quarkus.flyway.test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.enterprise.inject.CreationException; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +import org.flywaydb.core.Flyway; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.flyway.FlywayDataSource; +import io.quarkus.test.QuarkusUnitTest; + +public class FlywayExtensionMigrateAtStartNamedDatasourceConfigActiveFalseTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource("db/migration/V1.0.0__Quarkus.sql")) + .overrideConfigKey("quarkus.datasource.users.active", "false") + .overrideConfigKey("quarkus.flyway.users.migrate-at-start", "true") + // 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") + // We need this otherwise the *default* datasource may impact this test + .overrideConfigKey("quarkus.datasource.db-kind", "h2") + .overrideConfigKey("quarkus.datasource.username", "sa") + .overrideConfigKey("quarkus.datasource.password", "sa") + .overrideConfigKey("quarkus.datasource.jdbc.url", + "jdbc:h2:tcp://localhost/mem:test-quarkus-migrate-at-start;DB_CLOSE_DELAY=-1"); + + @Inject + @FlywayDataSource("users") + Instance flywayForNamedDatasource; + + @Test + @DisplayName("If a named datasource is deactivated, even if migrate-at-start is enabled, the application should boot, but Flyway should be deactivated for that datasource") + public void testBootSucceedsButFlywayDeactivated() { + assertThatThrownBy(flywayForNamedDatasource::get) + .isInstanceOf(CreationException.class) + .cause() + .hasMessageContainingAll("Unable to find datasource 'users' for Flyway", + "Datasource 'users' was deactivated through configuration properties.", + "To solve this, avoid accessing this datasource at runtime", + "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."); + } +} 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 new file mode 100644 index 0000000000000..34e6a4629f295 --- /dev/null +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainerUtil.java @@ -0,0 +1,44 @@ +package io.quarkus.flyway.runtime; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; + +import jakarta.enterprise.inject.Default; + +import io.quarkus.agroal.runtime.DataSources; +import io.quarkus.arc.Arc; +import io.quarkus.arc.InstanceHandle; +import io.quarkus.datasource.common.runtime.DataSourceUtil; +import io.quarkus.flyway.FlywayDataSource; + +public final class FlywayContainerUtil { + private FlywayContainerUtil() { + } + + public static FlywayContainer getFlywayContainer(String dataSourceName) { + return Arc.container().instance(FlywayContainer.class, + getFlywayContainerQualifier(dataSourceName)).get(); + } + + public static List getActiveFlywayContainers() { + List result = new ArrayList<>(); + for (String datasourceName : Arc.container().instance(DataSources.class).get().getActiveDataSourceNames()) { + InstanceHandle handle = Arc.container().instance(FlywayContainer.class, + getFlywayContainerQualifier(datasourceName)); + if (!handle.isAvailable()) { + continue; + } + result.add(handle.get()); + } + return result; + } + + public static Annotation getFlywayContainerQualifier(String dataSourceName) { + if (DataSourceUtil.isDefault(dataSourceName)) { + return Default.Literal.INSTANCE; + } + + return FlywayDataSource.FlywayDataSourceLiteral.of(dataSourceName); + } +} diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainersSupplier.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainersSupplier.java index 11261c88c30a1..d44d997f01964 100644 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainersSupplier.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayContainersSupplier.java @@ -2,29 +2,18 @@ import java.util.Collection; import java.util.Comparator; -import java.util.List; import java.util.Set; import java.util.TreeSet; import java.util.function.Supplier; -import io.quarkus.arc.Arc; -import io.quarkus.arc.InstanceHandle; import io.quarkus.datasource.common.runtime.DataSourceUtil; public class FlywayContainersSupplier implements Supplier> { @Override public Collection get() { - List> flywayContainerHandles = Arc.container().listAll(FlywayContainer.class); - - if (flywayContainerHandles.isEmpty()) { - return Set.of(); - } - Set containers = new TreeSet<>(FlywayContainerComparator.INSTANCE); - for (InstanceHandle flywayContainerHandle : flywayContainerHandles) { - containers.add(flywayContainerHandle.get()); - } + containers.addAll(FlywayContainerUtil.getActiveFlywayContainers()); return containers; } diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java index 726ada0de89fd..2e9d9aa3dd35e 100644 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java @@ -25,8 +25,8 @@ public static FlywayDataSourceRuntimeConfig defaultConfig() { /** * Flag to activate/deactivate Flyway for a specific datasource at runtime. */ - @ConfigItem(defaultValue = "true") - public boolean active = true; + @ConfigItem(defaultValueDocumentation = "'true' if the datasource is active; 'false' otherwise") + public Optional active = Optional.empty(); /** * The maximum number of retries when attempting to connect to the database. 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 4e858dcf71a3b..3af211f682891 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,5 @@ package io.quarkus.flyway.runtime; -import java.lang.annotation.Annotation; import java.util.Collection; import java.util.Locale; import java.util.Map; @@ -8,8 +7,6 @@ import javax.sql.DataSource; -import jakarta.enterprise.inject.Default; - import org.flywaydb.core.Flyway; import org.flywaydb.core.FlywayExecutor; import org.flywaydb.core.api.callback.Callback; @@ -29,7 +26,6 @@ import io.quarkus.arc.InstanceHandle; import io.quarkus.arc.SyntheticCreationalContext; import io.quarkus.datasource.common.runtime.DataSourceUtil; -import io.quarkus.flyway.FlywayDataSource.FlywayDataSourceLiteral; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.annotations.Recorder; import io.quarkus.runtime.configuration.ConfigurationException; @@ -73,7 +69,7 @@ public FlywayContainer apply(SyntheticCreationalContext context throw DataSourceUtil.dataSourceNotConfigured(dataSourceName); } } catch (ConfigurationException e) { - // TODO do we really want to enable retrieval of a FlywayContainer for an unconfigured datasource? + // 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) // would probably make more sense. @@ -93,7 +89,7 @@ public Function, Flyway> flywayFunction(Strin @Override public Flyway apply(SyntheticCreationalContext context) { FlywayContainer flywayContainer = context.getInjectedReference(FlywayContainer.class, - getFlywayContainerQualifier(dataSourceName)); + FlywayContainerUtil.getFlywayContainerQualifier(dataSourceName)); return flywayContainer.getFlyway(); } }; @@ -103,12 +99,15 @@ public void doStartActions(String dataSourceName) { FlywayDataSourceRuntimeConfig flywayDataSourceRuntimeConfig = config.getValue() .getConfigForDataSourceName(dataSourceName); - if (!config.getValue().getConfigForDataSourceName(dataSourceName).active) { + 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))) { return; } InstanceHandle flywayContainerInstanceHandle = Arc.container().instance(FlywayContainer.class, - getFlywayContainerQualifier(dataSourceName)); + FlywayContainerUtil.getFlywayContainerQualifier(dataSourceName)); if (!flywayContainerInstanceHandle.isAvailable()) { return; @@ -138,14 +137,6 @@ public void doStartActions(String dataSourceName) { } } - private static Annotation getFlywayContainerQualifier(String dataSourceName) { - if (DataSourceUtil.isDefault(dataSourceName)) { - return Default.Literal.INSTANCE; - } - - return FlywayDataSourceLiteral.of(dataSourceName); - } - static class BaselineCommand implements FlywayExecutor.Command { BaselineCommand(Flyway flyway) { this.flyway = flyway; diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywaySchemaProvider.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywaySchemaProvider.java index 57dab412c16d1..529b80a67c33e 100644 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywaySchemaProvider.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywaySchemaProvider.java @@ -1,23 +1,19 @@ package io.quarkus.flyway.runtime; -import io.quarkus.arc.Arc; import io.quarkus.datasource.runtime.DatabaseSchemaProvider; public class FlywaySchemaProvider implements DatabaseSchemaProvider { @Override public void resetDatabase(String dbName) { - for (FlywayContainer flywayContainer : Arc.container().select(FlywayContainer.class)) { - if (flywayContainer.getDataSourceName().equals(dbName)) { - flywayContainer.getFlyway().clean(); - flywayContainer.getFlyway().migrate(); - } - } + FlywayContainer flywayContainer = FlywayContainerUtil.getFlywayContainer(dbName); + flywayContainer.getFlyway().clean(); + flywayContainer.getFlyway().migrate(); } @Override public void resetAllDatabases() { - for (FlywayContainer flywayContainer : Arc.container().select(FlywayContainer.class)) { + for (FlywayContainer flywayContainer : FlywayContainerUtil.getActiveFlywayContainers()) { flywayContainer.getFlyway().clean(); flywayContainer.getFlyway().migrate(); } diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithExplicitDatasourceConfigActiveFalseTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithExplicitDatasourceConfigActiveFalseTest.java new file mode 100644 index 0000000000000..570fdd3e6368f --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithExplicitDatasourceConfigActiveFalseTest.java @@ -0,0 +1,41 @@ +package io.quarkus.hibernate.orm.config.datasource; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.hibernate.orm.config.MyEntity; +import io.quarkus.runtime.configuration.ConfigurationException; +import io.quarkus.test.QuarkusUnitTest; + +public class EntitiesInDefaultPUWithExplicitDatasourceConfigActiveFalseTest { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClass(MyEntity.class)) + .overrideConfigKey("quarkus.hibernate-orm.datasource", "ds-1") + .overrideConfigKey("quarkus.hibernate-orm.database.generation", "drop-and-create") + .overrideConfigKey("quarkus.datasource.\"ds-1\".active", "false") + // We need at least one build-time property for the datasource, + // otherwise it's considered unconfigured at build time... + .overrideConfigKey("quarkus.datasource.\"ds-1\".db-kind", "h2") + .assertException(t -> assertThat(t) + .isInstanceOf(ConfigurationException.class) + .hasMessageContainingAll( + "Unable to find datasource 'ds-1' for persistence unit ''", + "Datasource 'ds-1' 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.\"ds-1\".active'" + + " to 'true' and configure datasource 'ds-1'", + "Refer to https://quarkus.io/guides/datasource for guidance.")); + + @Test + public void testInvalidConfiguration() { + // deployment exception should happen first + Assertions.fail(); + } + +} diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitDatasourceConfigActiveFalseTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitDatasourceConfigActiveFalseTest.java new file mode 100644 index 0000000000000..04e0448723341 --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitDatasourceConfigActiveFalseTest.java @@ -0,0 +1,36 @@ +package io.quarkus.hibernate.orm.config.datasource; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.hibernate.orm.config.MyEntity; +import io.quarkus.runtime.configuration.ConfigurationException; +import io.quarkus.test.QuarkusUnitTest; + +public class EntitiesInDefaultPUWithImplicitDatasourceConfigActiveFalseTest { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClass(MyEntity.class)) + .overrideConfigKey("quarkus.datasource.active", "false") + .assertException(t -> assertThat(t) + .isInstanceOf(ConfigurationException.class) + .hasMessageContainingAll( + "Unable to find datasource '' for persistence unit ''", + "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 testInvalidConfiguration() { + // deployment exception should happen first + Assertions.fail(); + } + +} diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInNamedPUWithExplicitDatasourceConfigActiveFalseTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInNamedPUWithExplicitDatasourceConfigActiveFalseTest.java new file mode 100644 index 0000000000000..8f5f8c6904fc5 --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInNamedPUWithExplicitDatasourceConfigActiveFalseTest.java @@ -0,0 +1,41 @@ +package io.quarkus.hibernate.orm.config.datasource; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.hibernate.orm.config.namedpu.MyEntity; +import io.quarkus.runtime.configuration.ConfigurationException; +import io.quarkus.test.QuarkusUnitTest; + +public class EntitiesInNamedPUWithExplicitDatasourceConfigActiveFalseTest { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addPackage(MyEntity.class.getPackage().getName())) + .overrideConfigKey("quarkus.hibernate-orm.pu-1.datasource", "ds-1") + .overrideConfigKey("quarkus.hibernate-orm.pu-1.database.generation", "drop-and-create") + .overrideConfigKey("quarkus.datasource.\"ds-1\".active", "false") + // We need at least one build-time property for the datasource, + // otherwise it's considered unconfigured at build time... + .overrideConfigKey("quarkus.datasource.\"ds-1\".db-kind", "h2") + .assertException(t -> assertThat(t) + .isInstanceOf(ConfigurationException.class) + .hasMessageContainingAll( + "Unable to find datasource 'ds-1' for persistence unit 'pu-1'", + "Datasource 'ds-1' 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.\"ds-1\".active'" + + " to 'true' and configure datasource 'ds-1'", + "Refer to https://quarkus.io/guides/datasource for guidance.")); + + @Test + public void testInvalidConfiguration() { + // deployment exception should happen first + Assertions.fail(); + } + +} 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 8c2a597126e5f..ab68ddd370514 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 @@ -306,10 +306,13 @@ ServiceStartBuildItem startLiquibase(LiquibaseRecorder recorder, BuildProducer initializationCompleteBuildItem, BuildProducer schemaReadyBuildItem) { - recorder.doStartActions(); + Set dataSourceNames = getDataSourceNames(jdbcDataSourceBuildItems); + for (String dataSourceName : dataSourceNames) { + recorder.doStartActions(dataSourceName); + } // once we are done running the migrations, we produce a build item indicating that the // schema is "ready" - schemaReadyBuildItem.produce(new JdbcDataSourceSchemaReadyBuildItem(getDataSourceNames(jdbcDataSourceBuildItems))); + schemaReadyBuildItem.produce(new JdbcDataSourceSchemaReadyBuildItem(dataSourceNames)); initializationCompleteBuildItem.produce(new InitTaskCompletedBuildItem("liquibase")); return new ServiceStartBuildItem("liquibase"); diff --git a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigActiveFalseDefaultDatasourceTest.java b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigActiveFalseDefaultDatasourceTest.java new file mode 100644 index 0000000000000..ed35e12bfd237 --- /dev/null +++ b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigActiveFalseDefaultDatasourceTest.java @@ -0,0 +1,39 @@ +package io.quarkus.liquibase.test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.enterprise.inject.CreationException; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.liquibase.LiquibaseFactory; +import io.quarkus.test.QuarkusUnitTest; + +public class LiquibaseExtensionConfigActiveFalseDefaultDatasourceTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .overrideConfigKey("quarkus.datasource.active", "false"); + + @Inject + Instance liquibaseForDefaultDatasource; + + @Test + @DisplayName("If the default datasource is deactivated, the application should boot, but Liquibase should be deactivated for that datasource") + public void testBootSucceedsButLiquibaseDeactivated() { + assertThatThrownBy(() -> liquibaseForDefaultDatasource.get().getConfiguration()) + .isInstanceOf(CreationException.class) + .cause() + .hasMessageContainingAll("Unable to find datasource '' for Liquibase", + "Datasource '' was deactivated through configuration properties.", + "To solve this, avoid accessing this datasource at runtime", + "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."); + } + +} diff --git a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigActiveFalseNamedDatasourceTest.java b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigActiveFalseNamedDatasourceTest.java new file mode 100644 index 0000000000000..583e2d934dadc --- /dev/null +++ b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigActiveFalseNamedDatasourceTest.java @@ -0,0 +1,48 @@ +package io.quarkus.liquibase.test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.enterprise.inject.CreationException; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.liquibase.LiquibaseDataSource; +import io.quarkus.liquibase.LiquibaseFactory; +import io.quarkus.test.QuarkusUnitTest; + +public class LiquibaseExtensionConfigActiveFalseNamedDatasourceTest { + + @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") + // We need this otherwise it's going to be the *default* datasource making everything fail + .overrideConfigKey("quarkus.datasource.db-kind", "h2") + .overrideConfigKey("quarkus.datasource.username", "sa") + .overrideConfigKey("quarkus.datasource.password", "sa") + .overrideConfigKey("quarkus.datasource.jdbc.url", + "jdbc:h2:tcp://localhost/mem:test-quarkus-migrate-at-start;DB_CLOSE_DELAY=-1"); + @Inject + @LiquibaseDataSource("users") + Instance liquibaseForNamedDatasource; + + @Test + @DisplayName("If a named datasource is deactivated, the application should boot, but Liquibase should be deactivated for that datasource") + public void testBootSucceedsButLiquibaseDeactivated() { + assertThatThrownBy(() -> liquibaseForNamedDatasource.get().getConfiguration()) + .isInstanceOf(CreationException.class) + .cause() + .hasMessageContainingAll("Unable to find datasource 'users' for Liquibase", + "Datasource 'users' was deactivated through configuration properties.", + "To solve this, avoid accessing this datasource at runtime", + "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."); + } +} diff --git a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigActiveFalseTest.java b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigActiveFalseTest.java new file mode 100644 index 0000000000000..dccc77efe2e06 --- /dev/null +++ b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigActiveFalseTest.java @@ -0,0 +1,42 @@ +package io.quarkus.liquibase.test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.enterprise.inject.CreationException; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.liquibase.LiquibaseFactory; +import io.quarkus.test.QuarkusUnitTest; + +public class LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigActiveFalseTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource("db/changeLog.xml", "db/changeLog.xml")) + .overrideConfigKey("quarkus.datasource.active", "false") + .overrideConfigKey("quarkus.liquibase.migrate-at-start", "true"); + + @Inject + Instance liquibaseForDefaultDatasource; + + @Test + @DisplayName("If the default datasource is deactivated, even if migrate-at-start is enabled, the application should boot, but Liquibase should be deactivated for that datasource") + public void testBootSucceedsButLiquibaseDeactivated() { + assertThatThrownBy(() -> liquibaseForDefaultDatasource.get().getConfiguration()) + .isInstanceOf(CreationException.class) + .cause() + .hasMessageContainingAll("Unable to find datasource '' for Liquibase", + "Datasource '' was deactivated through configuration properties.", + "To solve this, avoid accessing this datasource at runtime", + "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."); + } + +} diff --git a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartNamedDatasourceConfigActiveFalseTest.java b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartNamedDatasourceConfigActiveFalseTest.java new file mode 100644 index 0000000000000..bfc68585954d6 --- /dev/null +++ b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartNamedDatasourceConfigActiveFalseTest.java @@ -0,0 +1,52 @@ +package io.quarkus.liquibase.test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.enterprise.inject.CreationException; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.liquibase.LiquibaseDataSource; +import io.quarkus.liquibase.LiquibaseFactory; +import io.quarkus.test.QuarkusUnitTest; + +public class LiquibaseExtensionMigrateAtStartNamedDatasourceConfigActiveFalseTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource("db/changeLog.xml", "db/changeLog.xml")) + .overrideConfigKey("quarkus.datasource.users.active", "false") + .overrideConfigKey("quarkus.liquibase.users.migrate-at-start", "true") + // 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") + // We need this otherwise it's going to be the *default* datasource making everything fail + .overrideConfigKey("quarkus.datasource.db-kind", "h2") + .overrideConfigKey("quarkus.datasource.username", "sa") + .overrideConfigKey("quarkus.datasource.password", "sa") + .overrideConfigKey("quarkus.datasource.jdbc.url", + "jdbc:h2:tcp://localhost/mem:test-quarkus-migrate-at-start;DB_CLOSE_DELAY=-1"); + + @Inject + @LiquibaseDataSource("users") + Instance liquibaseForNamedDatasource; + + @Test + @DisplayName("If a named datasource is deactivated, even if migrate-at-start is enabled, the application should boot, but Liquibase should be deactivated for that datasource") + public void testBootSucceedsButLiquibaseDeactivated() { + assertThatThrownBy(() -> liquibaseForNamedDatasource.get().getConfiguration()) + .isInstanceOf(CreationException.class) + .cause() + .hasMessageContainingAll("Unable to find datasource 'users' for Liquibase", + "Datasource 'users' was deactivated through configuration properties.", + "To solve this, avoid accessing this datasource at runtime", + "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."); + } +} 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 new file mode 100644 index 0000000000000..c3d4698cfe4c6 --- /dev/null +++ b/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseFactoryUtil.java @@ -0,0 +1,45 @@ +package io.quarkus.liquibase.runtime; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; + +import jakarta.enterprise.inject.Default; + +import io.quarkus.agroal.runtime.DataSources; +import io.quarkus.arc.Arc; +import io.quarkus.arc.InstanceHandle; +import io.quarkus.datasource.common.runtime.DataSourceUtil; +import io.quarkus.liquibase.LiquibaseDataSource; +import io.quarkus.liquibase.LiquibaseFactory; + +public final class LiquibaseFactoryUtil { + private LiquibaseFactoryUtil() { + } + + public static InstanceHandle getLiquibaseFactory(String dataSourceName) { + return Arc.container().instance(LiquibaseFactory.class, + getLiquibaseFactoryQualifier(dataSourceName)); + } + + public static List> getActiveLiquibaseFactories() { + List> result = new ArrayList<>(); + for (String datasourceName : Arc.container().instance(DataSources.class).get().getActiveDataSourceNames()) { + InstanceHandle handle = Arc.container().instance(LiquibaseFactory.class, + getLiquibaseFactoryQualifier(datasourceName)); + if (!handle.isAvailable()) { + continue; + } + result.add(handle); + } + return result; + } + + public static Annotation getLiquibaseFactoryQualifier(String dataSourceName) { + if (DataSourceUtil.isDefault(dataSourceName)) { + return Default.Literal.INSTANCE; + } + + return LiquibaseDataSource.LiquibaseDataSourceLiteral.of(dataSourceName); + } +} 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 4b045a357417b..f368806383203 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 @@ -5,13 +5,11 @@ import javax.sql.DataSource; -import jakarta.enterprise.inject.Any; import jakarta.enterprise.inject.UnsatisfiedResolutionException; import io.quarkus.agroal.runtime.DataSources; import io.quarkus.agroal.runtime.UnconfiguredDataSource; import io.quarkus.arc.Arc; -import io.quarkus.arc.InjectableInstance; import io.quarkus.arc.InstanceHandle; import io.quarkus.arc.SyntheticCreationalContext; import io.quarkus.datasource.common.runtime.DataSourceUtil; @@ -52,49 +50,45 @@ public LiquibaseFactory apply(SyntheticCreationalContext conte }; } - public void doStartActions() { + 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 { - InjectableInstance liquibaseFactoryInstance = Arc.container() - .select(LiquibaseFactory.class, Any.Literal.INSTANCE); - if (liquibaseFactoryInstance.isUnsatisfied()) { + LiquibaseFactory liquibaseFactory = liquibaseFactoryHandle.get(); + var config = liquibaseFactory.getConfiguration(); + if (!config.cleanAtStart && !config.migrateAtStart) { return; } - - for (InstanceHandle liquibaseFactoryHandle : liquibaseFactoryInstance.handles()) { - try { - LiquibaseFactory liquibaseFactory = liquibaseFactoryHandle.get(); - var config = liquibaseFactory.getConfiguration(); - if (!config.cleanAtStart && !config.migrateAtStart) { - continue; - } - try (Liquibase liquibase = liquibaseFactory.createLiquibase()) { - if (config.cleanAtStart) { - liquibase.dropAll(); - } - if (config.migrateAtStart) { - var lockService = LockServiceFactory.getInstance() - .getLockService(liquibase.getDatabase()); - lockService.waitForLock(); - try { - if (config.validateOnMigrate) { - liquibase.validate(); - } - liquibase.update(liquibaseFactory.createContexts(), liquibaseFactory.createLabels()); - } finally { - lockService.releaseLock(); - } + try (Liquibase liquibase = liquibaseFactory.createLiquibase()) { + if (config.cleanAtStart) { + liquibase.dropAll(); + } + if (config.migrateAtStart) { + var lockService = LockServiceFactory.getInstance() + .getLockService(liquibase.getDatabase()); + lockService.waitForLock(); + try { + if (config.validateOnMigrate) { + liquibase.validate(); } + liquibase.update(liquibaseFactory.createContexts(), liquibaseFactory.createLabels()); + } finally { + lockService.releaseLock(); } - } catch (UnsatisfiedResolutionException e) { - //ignore, the DS is not configured } } + } catch (UnsatisfiedResolutionException e) { + //ignore, the DS is not configured } catch (Exception e) { throw new IllegalStateException("Error starting Liquibase", e); } } + } diff --git a/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseSchemaProvider.java b/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseSchemaProvider.java index d3bc30ca0ed90..35701758bd6f1 100644 --- a/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseSchemaProvider.java +++ b/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseSchemaProvider.java @@ -1,10 +1,7 @@ package io.quarkus.liquibase.runtime; -import jakarta.enterprise.inject.Any; import jakarta.enterprise.inject.UnsatisfiedResolutionException; -import io.quarkus.arc.Arc; -import io.quarkus.arc.InjectableInstance; import io.quarkus.arc.InstanceHandle; import io.quarkus.datasource.runtime.DatabaseSchemaProvider; import io.quarkus.liquibase.LiquibaseFactory; @@ -15,20 +12,11 @@ public class LiquibaseSchemaProvider implements DatabaseSchemaProvider { @Override public void resetDatabase(String dbName) { try { - InjectableInstance liquibaseFactoryInstance = Arc.container() - .select(LiquibaseFactory.class, Any.Literal.INSTANCE); - if (liquibaseFactoryInstance.isUnsatisfied()) { - return; - } - for (InstanceHandle liquibaseFactoryHandle : liquibaseFactoryInstance.handles()) { - try { - LiquibaseFactory liquibaseFactory = liquibaseFactoryHandle.get(); - if (liquibaseFactory.getDataSourceName().equals(dbName)) { - doReset(liquibaseFactory); - } - } catch (UnsatisfiedResolutionException e) { - //ignore, the DS is not configured - } + try { + LiquibaseFactory liquibaseFactory = LiquibaseFactoryUtil.getLiquibaseFactory(dbName).get(); + doReset(liquibaseFactory); + } catch (UnsatisfiedResolutionException e) { + //ignore, the DS is not configured } } catch (Exception e) { throw new IllegalStateException("Error starting Liquibase", e); @@ -38,12 +26,7 @@ public void resetDatabase(String dbName) { @Override public void resetAllDatabases() { try { - InjectableInstance liquibaseFactoryInstance = Arc.container() - .select(LiquibaseFactory.class, Any.Literal.INSTANCE); - if (liquibaseFactoryInstance.isUnsatisfied()) { - return; - } - for (InstanceHandle liquibaseFactoryHandle : liquibaseFactoryInstance.handles()) { + for (InstanceHandle liquibaseFactoryHandle : LiquibaseFactoryUtil.getActiveLiquibaseFactories()) { try { LiquibaseFactory liquibaseFactory = liquibaseFactoryHandle.get(); doReset(liquibaseFactory); diff --git a/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/devui/LiquibaseFactoriesSupplier.java b/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/devui/LiquibaseFactoriesSupplier.java index ccd6b43863542..87a10e56483fd 100644 --- a/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/devui/LiquibaseFactoriesSupplier.java +++ b/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/devui/LiquibaseFactoriesSupplier.java @@ -1,35 +1,25 @@ package io.quarkus.liquibase.runtime.devui; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.Set; import java.util.TreeSet; import java.util.function.Supplier; -import jakarta.enterprise.inject.Any; - -import io.quarkus.arc.Arc; -import io.quarkus.arc.InjectableInstance; import io.quarkus.arc.InstanceHandle; import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.liquibase.LiquibaseFactory; +import io.quarkus.liquibase.runtime.LiquibaseFactoryUtil; public class LiquibaseFactoriesSupplier implements Supplier> { @Override public Collection get() { - InjectableInstance liquibaseFactoryInstance = Arc.container().select(LiquibaseFactory.class, - Any.Literal.INSTANCE); - if (liquibaseFactoryInstance.isUnsatisfied()) { - return Collections.emptySet(); - } - - Set liquibaseFactories = new TreeSet<>(LiquibaseFactoryComparator.INSTANCE); - for (InstanceHandle liquibaseFactoryHandle : liquibaseFactoryInstance.handles()) { - liquibaseFactories.add(liquibaseFactoryHandle.get()); + Set containers = new TreeSet<>(LiquibaseFactoryComparator.INSTANCE); + for (InstanceHandle handle : LiquibaseFactoryUtil.getActiveLiquibaseFactories()) { + containers.add(handle.get()); } - return liquibaseFactories; + return containers; } private static class LiquibaseFactoryComparator implements Comparator { diff --git a/extensions/reactive-db2-client/deployment/src/main/java/io/quarkus/reactive/db2/client/deployment/ReactiveDB2ClientProcessor.java b/extensions/reactive-db2-client/deployment/src/main/java/io/quarkus/reactive/db2/client/deployment/ReactiveDB2ClientProcessor.java index 60187addf5680..4c94cc18e68c5 100644 --- a/extensions/reactive-db2-client/deployment/src/main/java/io/quarkus/reactive/db2/client/deployment/ReactiveDB2ClientProcessor.java +++ b/extensions/reactive-db2-client/deployment/src/main/java/io/quarkus/reactive/db2/client/deployment/ReactiveDB2ClientProcessor.java @@ -32,6 +32,7 @@ import io.quarkus.datasource.deployment.spi.DefaultDataSourceDbKindBuildItem; import io.quarkus.datasource.deployment.spi.DevServicesDatasourceConfigurationHandlerBuildItem; import io.quarkus.datasource.runtime.DataSourceBuildTimeConfig; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.datasource.runtime.DataSourcesBuildTimeConfig; import io.quarkus.datasource.runtime.DataSourcesRuntimeConfig; import io.quarkus.deployment.Capabilities; @@ -208,6 +209,7 @@ private void createPoolIfDefined(DB2PoolRecorder recorder, .addType(Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) + .addInjectionPoint(ClassType.create(DataSourceSupport.class)) .createWith(poolFunction) .unremovable() .setRuntimeInit(); @@ -222,6 +224,7 @@ private void createPoolIfDefined(DB2PoolRecorder recorder, .addType(io.vertx.mutiny.sqlclient.Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) + .addInjectionPoint(ClassType.create(DataSourceSupport.class)) .createWith(recorder.mutinyDB2Pool(poolFunction)) .unremovable() .setRuntimeInit(); diff --git a/extensions/reactive-db2-client/runtime/src/main/java/io/quarkus/reactive/db2/client/runtime/DB2PoolRecorder.java b/extensions/reactive-db2-client/runtime/src/main/java/io/quarkus/reactive/db2/client/runtime/DB2PoolRecorder.java index d5e372b88779b..85e16402885f6 100644 --- a/extensions/reactive-db2-client/runtime/src/main/java/io/quarkus/reactive/db2/client/runtime/DB2PoolRecorder.java +++ b/extensions/reactive-db2-client/runtime/src/main/java/io/quarkus/reactive/db2/client/runtime/DB2PoolRecorder.java @@ -25,6 +25,7 @@ import io.quarkus.credentials.runtime.CredentialsProviderFinder; import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.datasource.runtime.DataSourceRuntimeConfig; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.datasource.runtime.DataSourcesRuntimeConfig; import io.quarkus.reactive.datasource.ReactiveDataSource; import io.quarkus.reactive.datasource.runtime.ConnectOptionsSupplier; @@ -91,6 +92,9 @@ private DB2Pool initialize(VertxInternal vertx, DataSourceReactiveRuntimeConfig dataSourceReactiveRuntimeConfig, DataSourceReactiveDB2Config dataSourceReactiveDB2Config, SyntheticCreationalContext context) { + if (context.getInjectedReference(DataSourceSupport.class).getInactiveNames().contains(dataSourceName)) { + throw DataSourceUtil.dataSourceInactive(dataSourceName); + } PoolOptions poolOptions = toPoolOptions(eventLoopCount, dataSourceRuntimeConfig, dataSourceReactiveRuntimeConfig, dataSourceReactiveDB2Config); DB2ConnectOptions db2ConnectOptions = toConnectOptions(dataSourceName, dataSourceRuntimeConfig, diff --git a/extensions/reactive-db2-client/runtime/src/main/java/io/quarkus/reactive/db2/client/runtime/health/ReactiveDB2DataSourcesHealthCheck.java b/extensions/reactive-db2-client/runtime/src/main/java/io/quarkus/reactive/db2/client/runtime/health/ReactiveDB2DataSourcesHealthCheck.java index 72c046f4c33cb..fe4d3a860c1a7 100644 --- a/extensions/reactive-db2-client/runtime/src/main/java/io/quarkus/reactive/db2/client/runtime/health/ReactiveDB2DataSourcesHealthCheck.java +++ b/extensions/reactive-db2-client/runtime/src/main/java/io/quarkus/reactive/db2/client/runtime/health/ReactiveDB2DataSourcesHealthCheck.java @@ -20,7 +20,7 @@ import io.quarkus.arc.ArcContainer; import io.quarkus.arc.InstanceHandle; import io.quarkus.datasource.common.runtime.DataSourceUtil; -import io.quarkus.datasource.runtime.DataSourcesHealthSupport; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.reactive.datasource.ReactiveDataSource; import io.vertx.mutiny.db2client.DB2Pool; @@ -37,8 +37,8 @@ class ReactiveDB2DataSourcesHealthCheck implements HealthCheck { @PostConstruct protected void init() { ArcContainer container = Arc.container(); - DataSourcesHealthSupport excluded = container.instance(DataSourcesHealthSupport.class).get(); - Set excludedNames = excluded.getExcludedNames(); + DataSourceSupport support = container.instance(DataSourceSupport.class).get(); + Set excludedNames = support.getInactiveOrHealthCheckExcludedNames(); for (InstanceHandle handle : container.select(DB2Pool.class, Any.Literal.INSTANCE).handles()) { String db2PoolName = getDB2PoolName(handle.getBean()); if (!excludedNames.contains(db2PoolName)) { diff --git a/extensions/reactive-mssql-client/deployment/src/main/java/io/quarkus/reactive/mssql/client/deployment/ReactiveMSSQLClientProcessor.java b/extensions/reactive-mssql-client/deployment/src/main/java/io/quarkus/reactive/mssql/client/deployment/ReactiveMSSQLClientProcessor.java index 2707e8aba1424..4dcafa3c6ab01 100644 --- a/extensions/reactive-mssql-client/deployment/src/main/java/io/quarkus/reactive/mssql/client/deployment/ReactiveMSSQLClientProcessor.java +++ b/extensions/reactive-mssql-client/deployment/src/main/java/io/quarkus/reactive/mssql/client/deployment/ReactiveMSSQLClientProcessor.java @@ -32,6 +32,7 @@ import io.quarkus.datasource.deployment.spi.DefaultDataSourceDbKindBuildItem; import io.quarkus.datasource.deployment.spi.DevServicesDatasourceConfigurationHandlerBuildItem; import io.quarkus.datasource.runtime.DataSourceBuildTimeConfig; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.datasource.runtime.DataSourcesBuildTimeConfig; import io.quarkus.datasource.runtime.DataSourcesRuntimeConfig; import io.quarkus.deployment.Capabilities; @@ -207,6 +208,7 @@ private void createPoolIfDefined(MSSQLPoolRecorder recorder, .addType(Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) + .addInjectionPoint(ClassType.create(DataSourceSupport.class)) .createWith(poolFunction) .unremovable() .setRuntimeInit(); @@ -221,6 +223,7 @@ private void createPoolIfDefined(MSSQLPoolRecorder recorder, .addType(io.vertx.mutiny.sqlclient.Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) + .addInjectionPoint(ClassType.create(DataSourceSupport.class)) .createWith(recorder.mutinyMSSQLPool(poolFunction)) .unremovable() .setRuntimeInit(); diff --git a/extensions/reactive-mssql-client/runtime/src/main/java/io/quarkus/reactive/mssql/client/runtime/MSSQLPoolRecorder.java b/extensions/reactive-mssql-client/runtime/src/main/java/io/quarkus/reactive/mssql/client/runtime/MSSQLPoolRecorder.java index dad7ed85e5f86..b3c2c8cf1da72 100644 --- a/extensions/reactive-mssql-client/runtime/src/main/java/io/quarkus/reactive/mssql/client/runtime/MSSQLPoolRecorder.java +++ b/extensions/reactive-mssql-client/runtime/src/main/java/io/quarkus/reactive/mssql/client/runtime/MSSQLPoolRecorder.java @@ -25,6 +25,7 @@ import io.quarkus.credentials.runtime.CredentialsProviderFinder; import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.datasource.runtime.DataSourceRuntimeConfig; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.datasource.runtime.DataSourcesRuntimeConfig; import io.quarkus.reactive.datasource.ReactiveDataSource; import io.quarkus.reactive.datasource.runtime.ConnectOptionsSupplier; @@ -61,6 +62,9 @@ public Function, MSSQLPool> configureMSSQL return new Function<>() { @Override public MSSQLPool apply(SyntheticCreationalContext context) { + if (context.getInjectedReference(DataSourceSupport.class).getInactiveNames().contains(dataSourceName)) { + throw DataSourceUtil.dataSourceInactive(dataSourceName); + } MSSQLPool pool = initialize((VertxInternal) vertx.getValue(), eventLoopCount.get(), dataSourceName, diff --git a/extensions/reactive-mssql-client/runtime/src/main/java/io/quarkus/reactive/mssql/client/runtime/health/ReactiveMSSQLDataSourcesHealthCheck.java b/extensions/reactive-mssql-client/runtime/src/main/java/io/quarkus/reactive/mssql/client/runtime/health/ReactiveMSSQLDataSourcesHealthCheck.java index 35537917a3399..a9a2c28a34685 100644 --- a/extensions/reactive-mssql-client/runtime/src/main/java/io/quarkus/reactive/mssql/client/runtime/health/ReactiveMSSQLDataSourcesHealthCheck.java +++ b/extensions/reactive-mssql-client/runtime/src/main/java/io/quarkus/reactive/mssql/client/runtime/health/ReactiveMSSQLDataSourcesHealthCheck.java @@ -11,7 +11,7 @@ import io.quarkus.arc.Arc; import io.quarkus.arc.ArcContainer; import io.quarkus.arc.InstanceHandle; -import io.quarkus.datasource.runtime.DataSourcesHealthSupport; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.reactive.datasource.runtime.ReactiveDatasourceHealthCheck; import io.vertx.mssqlclient.MSSQLPool; @@ -26,8 +26,8 @@ public ReactiveMSSQLDataSourcesHealthCheck() { @PostConstruct protected void init() { ArcContainer container = Arc.container(); - DataSourcesHealthSupport excluded = container.instance(DataSourcesHealthSupport.class).get(); - Set excludedNames = excluded.getExcludedNames(); + DataSourceSupport support = container.instance(DataSourceSupport.class).get(); + Set excludedNames = support.getInactiveOrHealthCheckExcludedNames(); for (InstanceHandle handle : container.select(MSSQLPool.class, Any.Literal.INSTANCE).handles()) { String poolName = getPoolName(handle.getBean()); if (!excludedNames.contains(poolName)) { diff --git a/extensions/reactive-mysql-client/deployment/src/main/java/io/quarkus/reactive/mysql/client/deployment/ReactiveMySQLClientProcessor.java b/extensions/reactive-mysql-client/deployment/src/main/java/io/quarkus/reactive/mysql/client/deployment/ReactiveMySQLClientProcessor.java index 4bb36e3156dd0..2d03f42ef6e7a 100644 --- a/extensions/reactive-mysql-client/deployment/src/main/java/io/quarkus/reactive/mysql/client/deployment/ReactiveMySQLClientProcessor.java +++ b/extensions/reactive-mysql-client/deployment/src/main/java/io/quarkus/reactive/mysql/client/deployment/ReactiveMySQLClientProcessor.java @@ -32,6 +32,7 @@ import io.quarkus.datasource.deployment.spi.DefaultDataSourceDbKindBuildItem; import io.quarkus.datasource.deployment.spi.DevServicesDatasourceConfigurationHandlerBuildItem; import io.quarkus.datasource.runtime.DataSourceBuildTimeConfig; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.datasource.runtime.DataSourcesBuildTimeConfig; import io.quarkus.datasource.runtime.DataSourcesRuntimeConfig; import io.quarkus.deployment.Capabilities; @@ -208,6 +209,7 @@ private void createPoolIfDefined(MySQLPoolRecorder recorder, .addType(Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) + .addInjectionPoint(ClassType.create(DataSourceSupport.class)) .createWith(poolFunction) .unremovable() .setRuntimeInit(); @@ -222,6 +224,7 @@ private void createPoolIfDefined(MySQLPoolRecorder recorder, .addType(io.vertx.mutiny.sqlclient.Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) + .addInjectionPoint(ClassType.create(DataSourceSupport.class)) .createWith(recorder.mutinyMySQLPool(poolFunction)) .unremovable() .setRuntimeInit(); diff --git a/extensions/reactive-mysql-client/runtime/src/main/java/io/quarkus/reactive/mysql/client/runtime/MySQLPoolRecorder.java b/extensions/reactive-mysql-client/runtime/src/main/java/io/quarkus/reactive/mysql/client/runtime/MySQLPoolRecorder.java index 8b0d101285326..fda8b5372f2a1 100644 --- a/extensions/reactive-mysql-client/runtime/src/main/java/io/quarkus/reactive/mysql/client/runtime/MySQLPoolRecorder.java +++ b/extensions/reactive-mysql-client/runtime/src/main/java/io/quarkus/reactive/mysql/client/runtime/MySQLPoolRecorder.java @@ -25,6 +25,7 @@ import io.quarkus.credentials.runtime.CredentialsProviderFinder; import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.datasource.runtime.DataSourceRuntimeConfig; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.datasource.runtime.DataSourcesRuntimeConfig; import io.quarkus.reactive.datasource.ReactiveDataSource; import io.quarkus.reactive.datasource.runtime.ConnectOptionsSupplier; @@ -91,6 +92,9 @@ private MySQLPool initialize(VertxInternal vertx, DataSourceReactiveRuntimeConfig dataSourceReactiveRuntimeConfig, DataSourceReactiveMySQLConfig dataSourceReactiveMySQLConfig, SyntheticCreationalContext context) { + if (context.getInjectedReference(DataSourceSupport.class).getInactiveNames().contains(dataSourceName)) { + throw DataSourceUtil.dataSourceInactive(dataSourceName); + } PoolOptions poolOptions = toPoolOptions(eventLoopCount, dataSourceRuntimeConfig, dataSourceReactiveRuntimeConfig, dataSourceReactiveMySQLConfig); List mySQLConnectOptions = toMySQLConnectOptions(dataSourceName, dataSourceRuntimeConfig, diff --git a/extensions/reactive-mysql-client/runtime/src/main/java/io/quarkus/reactive/mysql/client/runtime/health/ReactiveMySQLDataSourcesHealthCheck.java b/extensions/reactive-mysql-client/runtime/src/main/java/io/quarkus/reactive/mysql/client/runtime/health/ReactiveMySQLDataSourcesHealthCheck.java index 81470e5d9d0e6..656d585acce53 100644 --- a/extensions/reactive-mysql-client/runtime/src/main/java/io/quarkus/reactive/mysql/client/runtime/health/ReactiveMySQLDataSourcesHealthCheck.java +++ b/extensions/reactive-mysql-client/runtime/src/main/java/io/quarkus/reactive/mysql/client/runtime/health/ReactiveMySQLDataSourcesHealthCheck.java @@ -11,7 +11,7 @@ import io.quarkus.arc.Arc; import io.quarkus.arc.ArcContainer; import io.quarkus.arc.InstanceHandle; -import io.quarkus.datasource.runtime.DataSourcesHealthSupport; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.reactive.datasource.runtime.ReactiveDatasourceHealthCheck; import io.vertx.mysqlclient.MySQLPool; @@ -26,8 +26,8 @@ public ReactiveMySQLDataSourcesHealthCheck() { @PostConstruct protected void init() { ArcContainer container = Arc.container(); - DataSourcesHealthSupport excluded = container.instance(DataSourcesHealthSupport.class).get(); - Set excludedNames = excluded.getExcludedNames(); + DataSourceSupport support = container.instance(DataSourceSupport.class).get(); + Set excludedNames = support.getInactiveOrHealthCheckExcludedNames(); for (InstanceHandle handle : container.select(MySQLPool.class, Any.Literal.INSTANCE).handles()) { String poolName = getPoolName(handle.getBean()); if (!excludedNames.contains(poolName)) { diff --git a/extensions/reactive-oracle-client/deployment/src/main/java/io/quarkus/reactive/oracle/client/deployment/ReactiveOracleClientProcessor.java b/extensions/reactive-oracle-client/deployment/src/main/java/io/quarkus/reactive/oracle/client/deployment/ReactiveOracleClientProcessor.java index ab1cf2dceff79..ee5ed3fd99376 100644 --- a/extensions/reactive-oracle-client/deployment/src/main/java/io/quarkus/reactive/oracle/client/deployment/ReactiveOracleClientProcessor.java +++ b/extensions/reactive-oracle-client/deployment/src/main/java/io/quarkus/reactive/oracle/client/deployment/ReactiveOracleClientProcessor.java @@ -32,6 +32,7 @@ import io.quarkus.datasource.deployment.spi.DefaultDataSourceDbKindBuildItem; import io.quarkus.datasource.deployment.spi.DevServicesDatasourceConfigurationHandlerBuildItem; import io.quarkus.datasource.runtime.DataSourceBuildTimeConfig; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.datasource.runtime.DataSourcesBuildTimeConfig; import io.quarkus.datasource.runtime.DataSourcesRuntimeConfig; import io.quarkus.deployment.Capabilities; @@ -209,6 +210,7 @@ private void createPoolIfDefined(OraclePoolRecorder recorder, .addType(Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) + .addInjectionPoint(ClassType.create(DataSourceSupport.class)) .createWith(poolFunction) .unremovable() .setRuntimeInit(); @@ -223,6 +225,7 @@ private void createPoolIfDefined(OraclePoolRecorder recorder, .addType(io.vertx.mutiny.sqlclient.Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) + .addInjectionPoint(ClassType.create(DataSourceSupport.class)) .createWith(recorder.mutinyOraclePool(poolFunction)) .unremovable() .setRuntimeInit(); diff --git a/extensions/reactive-oracle-client/runtime/src/main/java/io/quarkus/reactive/oracle/client/runtime/OraclePoolRecorder.java b/extensions/reactive-oracle-client/runtime/src/main/java/io/quarkus/reactive/oracle/client/runtime/OraclePoolRecorder.java index dc49a6eafd261..54ef9588855e6 100644 --- a/extensions/reactive-oracle-client/runtime/src/main/java/io/quarkus/reactive/oracle/client/runtime/OraclePoolRecorder.java +++ b/extensions/reactive-oracle-client/runtime/src/main/java/io/quarkus/reactive/oracle/client/runtime/OraclePoolRecorder.java @@ -19,6 +19,7 @@ import io.quarkus.credentials.runtime.CredentialsProviderFinder; import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.datasource.runtime.DataSourceRuntimeConfig; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.datasource.runtime.DataSourcesRuntimeConfig; import io.quarkus.reactive.datasource.ReactiveDataSource; import io.quarkus.reactive.datasource.runtime.ConnectOptionsSupplier; @@ -87,6 +88,9 @@ private OraclePool initialize(VertxInternal vertx, DataSourceReactiveRuntimeConfig dataSourceReactiveRuntimeConfig, DataSourceReactiveOracleConfig dataSourceReactiveOracleConfig, SyntheticCreationalContext context) { + if (context.getInjectedReference(DataSourceSupport.class).getInactiveNames().contains(dataSourceName)) { + throw DataSourceUtil.dataSourceInactive(dataSourceName); + } PoolOptions poolOptions = toPoolOptions(eventLoopCount, dataSourceRuntimeConfig, dataSourceReactiveRuntimeConfig, dataSourceReactiveOracleConfig); OracleConnectOptions oracleConnectOptions = toOracleConnectOptions(dataSourceName, dataSourceRuntimeConfig, diff --git a/extensions/reactive-oracle-client/runtime/src/main/java/io/quarkus/reactive/oracle/client/runtime/health/ReactiveOracleDataSourcesHealthCheck.java b/extensions/reactive-oracle-client/runtime/src/main/java/io/quarkus/reactive/oracle/client/runtime/health/ReactiveOracleDataSourcesHealthCheck.java index fa462c96ac2b5..cc3370616f4eb 100644 --- a/extensions/reactive-oracle-client/runtime/src/main/java/io/quarkus/reactive/oracle/client/runtime/health/ReactiveOracleDataSourcesHealthCheck.java +++ b/extensions/reactive-oracle-client/runtime/src/main/java/io/quarkus/reactive/oracle/client/runtime/health/ReactiveOracleDataSourcesHealthCheck.java @@ -11,7 +11,7 @@ import io.quarkus.arc.Arc; import io.quarkus.arc.ArcContainer; import io.quarkus.arc.InstanceHandle; -import io.quarkus.datasource.runtime.DataSourcesHealthSupport; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.reactive.datasource.runtime.ReactiveDatasourceHealthCheck; import io.vertx.oracleclient.OraclePool; @@ -26,8 +26,8 @@ public ReactiveOracleDataSourcesHealthCheck() { @PostConstruct protected void init() { ArcContainer container = Arc.container(); - DataSourcesHealthSupport excluded = container.instance(DataSourcesHealthSupport.class).get(); - Set excludedNames = excluded.getExcludedNames(); + DataSourceSupport support = container.instance(DataSourceSupport.class).get(); + Set excludedNames = support.getInactiveOrHealthCheckExcludedNames(); for (InstanceHandle handle : container.select(OraclePool.class, Any.Literal.INSTANCE).handles()) { String poolName = getPoolName(handle.getBean()); if (!excludedNames.contains(poolName)) { diff --git a/extensions/reactive-pg-client/deployment/src/main/java/io/quarkus/reactive/pg/client/deployment/ReactivePgClientProcessor.java b/extensions/reactive-pg-client/deployment/src/main/java/io/quarkus/reactive/pg/client/deployment/ReactivePgClientProcessor.java index cce55cfa31a5c..9f87c5535811b 100644 --- a/extensions/reactive-pg-client/deployment/src/main/java/io/quarkus/reactive/pg/client/deployment/ReactivePgClientProcessor.java +++ b/extensions/reactive-pg-client/deployment/src/main/java/io/quarkus/reactive/pg/client/deployment/ReactivePgClientProcessor.java @@ -32,6 +32,7 @@ import io.quarkus.datasource.deployment.spi.DefaultDataSourceDbKindBuildItem; import io.quarkus.datasource.deployment.spi.DevServicesDatasourceConfigurationHandlerBuildItem; import io.quarkus.datasource.runtime.DataSourceBuildTimeConfig; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.datasource.runtime.DataSourcesBuildTimeConfig; import io.quarkus.datasource.runtime.DataSourcesRuntimeConfig; import io.quarkus.deployment.Capabilities; @@ -213,6 +214,7 @@ private void createPoolIfDefined(PgPoolRecorder recorder, .addType(Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) + .addInjectionPoint(ClassType.create(DataSourceSupport.class)) .createWith(poolFunction) .unremovable() .setRuntimeInit(); @@ -227,6 +229,7 @@ private void createPoolIfDefined(PgPoolRecorder recorder, .addType(io.vertx.mutiny.sqlclient.Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) + .addInjectionPoint(ClassType.create(DataSourceSupport.class)) .createWith(recorder.mutinyPgPool(poolFunction)) .unremovable() .setRuntimeInit(); diff --git a/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/PgPoolRecorder.java b/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/PgPoolRecorder.java index 18149f6e67b1a..4afab742f0163 100644 --- a/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/PgPoolRecorder.java +++ b/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/PgPoolRecorder.java @@ -24,6 +24,7 @@ import io.quarkus.credentials.runtime.CredentialsProviderFinder; import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.datasource.runtime.DataSourceRuntimeConfig; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.datasource.runtime.DataSourcesRuntimeConfig; import io.quarkus.reactive.datasource.ReactiveDataSource; import io.quarkus.reactive.datasource.runtime.ConnectOptionsSupplier; @@ -90,6 +91,9 @@ private PgPool initialize(VertxInternal vertx, DataSourceReactiveRuntimeConfig dataSourceReactiveRuntimeConfig, DataSourceReactivePostgreSQLConfig dataSourceReactivePostgreSQLConfig, SyntheticCreationalContext context) { + if (context.getInjectedReference(DataSourceSupport.class).getInactiveNames().contains(dataSourceName)) { + throw DataSourceUtil.dataSourceInactive(dataSourceName); + } PoolOptions poolOptions = toPoolOptions(eventLoopCount, dataSourceRuntimeConfig, dataSourceReactiveRuntimeConfig, dataSourceReactivePostgreSQLConfig); List pgConnectOptionsList = toPgConnectOptions(dataSourceName, dataSourceRuntimeConfig, diff --git a/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/health/ReactivePgDataSourcesHealthCheck.java b/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/health/ReactivePgDataSourcesHealthCheck.java index c0228a3c99869..9cfc47a61dc2a 100644 --- a/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/health/ReactivePgDataSourcesHealthCheck.java +++ b/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/health/ReactivePgDataSourcesHealthCheck.java @@ -11,7 +11,7 @@ import io.quarkus.arc.Arc; import io.quarkus.arc.ArcContainer; import io.quarkus.arc.InstanceHandle; -import io.quarkus.datasource.runtime.DataSourcesHealthSupport; +import io.quarkus.datasource.runtime.DataSourceSupport; import io.quarkus.reactive.datasource.runtime.ReactiveDatasourceHealthCheck; import io.vertx.pgclient.PgPool; @@ -26,8 +26,8 @@ public ReactivePgDataSourcesHealthCheck() { @PostConstruct protected void init() { ArcContainer container = Arc.container(); - DataSourcesHealthSupport excluded = container.instance(DataSourcesHealthSupport.class).get(); - Set excludedNames = excluded.getExcludedNames(); + DataSourceSupport support = container.instance(DataSourceSupport.class).get(); + Set excludedNames = support.getInactiveOrHealthCheckExcludedNames(); for (InstanceHandle handle : container.select(PgPool.class, Any.Literal.INSTANCE).handles()) { String poolName = getPoolName(handle.getBean()); if (!excludedNames.contains(poolName)) {