diff --git a/docs/src/main/asciidoc/cdi.adoc b/docs/src/main/asciidoc/cdi.adoc index 7d393c12f7b38..0cdc9da9d2fb3 100644 --- a/docs/src/main/asciidoc/cdi.adoc +++ b/docs/src/main/asciidoc/cdi.adoc @@ -253,7 +253,7 @@ Client proxies allow for: * Circular dependencies in the dependency graph. Having circular dependencies is often an indication that a redesign should be considered, but sometimes it's inevitable. * In rare cases it's practical to destroy the beans manually. A direct injected reference would lead to a stale bean instance. - +[[ok-you-said-that-there-are-several-kinds-of-beans]] == OK. You said that there are several kinds of beans? Yes. In general, we distinguish: diff --git a/docs/src/main/asciidoc/config-reference.adoc b/docs/src/main/asciidoc/config-reference.adoc index 8d68f8462abdf..286d6f5601177 100644 --- a/docs/src/main/asciidoc/config-reference.adoc +++ b/docs/src/main/asciidoc/config-reference.adoc @@ -329,6 +329,7 @@ By default, Quarkus provides three profiles, that activate automatically in cert * *test* - Activated when running tests * *prod* - The default profile when not running in development or test mode +[[custom-profiles]] === Custom Profiles It is also possible to create additional profiles and activate them with the `quarkus.profile` configuration property. A diff --git a/docs/src/main/asciidoc/datasource.adoc b/docs/src/main/asciidoc/datasource.adoc index 81fbb90de0eba..d2ebd461f7b4d 100644 --- a/docs/src/main/asciidoc/datasource.adoc +++ b/docs/src/main/asciidoc/datasource.adoc @@ -405,6 +405,99 @@ AgroalDataSource usersDataSource; AgroalDataSource inventoryDataSource; ---- +[[datasource-active]] +=== Activate/deactivate datasources + +If a datasource is configured at build time, +by default it is active at runtime, +that is Quarkus will start the corresponding JDBC connection pool or reactive client on application startup. + +To deactivate a datasource at runtime, set `quarkus.datasource[.optional name].active` to `false`. +Then Quarkus will not start the corresponding JDBC connection pool or reactive client on application startup. +Any attempt to use the corresponding datasource at runtime will fail with a clear error message. + +This is in particular useful when you want an application to be able +to use one of a pre-determined set of datasources at runtime. + +[WARNING] +==== +If another Quarkus extension relies on an inactive datasource, +that extension might fail to start. + +In such case, you will need to deactivate that other extension too. +For example see xref:hibernate-orm.adoc#persistence-unit-active[here for Hibernate ORM]. +==== + +For example, with the following configuration: + +[source,properties] +---- +quarkus.datasource."pg".db-kind=postgres +quarkus.datasource."pg".active=false +quarkus.datasource."pg".jdbc.url=jdbc:postgresql:///your_database + +quarkus.datasource."oracle".db-kind=oracle +quarkus.datasource."oracle".active=false +quarkus.datasource."oracle".jdbc.url=jdbc:oracle:///your_database +---- + +Setting `quarkus.datasource."pg".active=true` xref:config-reference.adoc#configuration-sources[at runtime] +will make only the PostgreSQL datasource available, +and setting `quarkus.datasource."oracle".active=true` at runtime +will make only the Oracle datasource available. + +[TIP] +==== +xref:config-reference.adoc#custom-profiles[Custom configuration profiles] can help simplify such a setup. +By appending the following profile-specific configuration to the one above, +you can select a persistence unit/datasource at runtime simply by +xref:config-reference.adoc#multiple-profiles[setting `quarkus.profile`]: +`quarkus.profile=prod,pg` or `quarkus.profile=prod,oracle`. + +[source,properties] +---- +%pg.quarkus.hibernate-orm."pg".active=true +%pg.quarkus.datasource."pg".active=true +# Add any pg-related runtime configuration here, prefixed with "%pg." + +%oracle.quarkus.hibernate-orm."oracle".active=true +%oracle.quarkus.datasource."oracle".active=true +# Add any pg-related runtime configuration here, prefixed with "%pg." +---- +==== + +[TIP] +==== +It can also be useful to define a xref:cdi.adoc#ok-you-said-that-there-are-several-kinds-of-beans[CDI bean producer] redirecting to the currently active datasource, +like this: + +[source,java,indent=0] +---- +public class MyProducer { + @Inject + DataSourceSupport dataSourceSupport; + + @Inject + @DataSource("pg") + AgroalDataSource pgDataSourceBean; + + @Inject + @DataSource("oracle") + AgroalDataSource oracleDataSourceBean; + + @Produces + @ApplicationScoped + public AgroalDataSource dataSource() { + if (dataSourceSupport.getInactiveNames().contains("pg")) { + return oracleDataSourceBean; + } else { + return pgDataSourceBean; + } + } +} +---- +==== + == Datasource integrations === Datasource health check diff --git a/docs/src/main/asciidoc/hibernate-orm.adoc b/docs/src/main/asciidoc/hibernate-orm.adoc index 3439cbae1e469..ff704928e292f 100644 --- a/docs/src/main/asciidoc/hibernate-orm.adoc +++ b/docs/src/main/asciidoc/hibernate-orm.adoc @@ -464,6 +464,98 @@ You can inject the `EntityManagerFactory` of a named persistence unit using the EntityManagerFactory entityManagerFactory; ---- +[[persistence-unit-active]] +=== Activate/deactivate persistence units + +If a persistence unit is configured at build time, +by default it is active at runtime, +that is Quarkus will start the corresponding Hibernate ORM `SessionFactory` on application startup. + +To deactivate a persistence unit at runtime, set `quarkus.hibernate-orm[.optional name].active` to `false`. +Then Quarkus will not start the corresponding Hibernate ORM `SessionFactory` on application startup. +Any attempt to use the corresponding persistence unit at runtime will fail with a clear error message. + +This is in particular useful when you want an application to be able +to xref:datasource.adoc#datasource-active[use one of a pre-determined set of datasources at runtime]. + +For example, with the following configuration: + +[source,properties] +---- +quarkus.hibernate-orm."pg".packages=org.acme.model.shared +quarkus.hibernate-orm."pg".datasource=pg +quarkus.hibernate-orm."pg".database.generation=drop-and-create +quarkus.hibernate-orm."pg".active=false +quarkus.datasource."pg".db-kind=h2 +quarkus.datasource."pg".active=false +quarkus.datasource."pg".jdbc.url=jdbc:postgresql:///your_database + +quarkus.hibernate-orm."oracle".packages=org.acme.model.shared +quarkus.hibernate-orm."oracle".datasource=oracle +quarkus.hibernate-orm."oracle".database.generation=drop-and-create +quarkus.hibernate-orm."oracle".active=false +quarkus.datasource."oracle".db-kind=oracle +quarkus.datasource."oracle".active=false +quarkus.datasource."oracle".jdbc.url=jdbc:oracle:///your_database +---- + +xref:config-reference.adoc#configuration-sources[Setting] `quarkus.hibernate-orm."pg".active=true` and `quarkus.datasource."pg".active=true` at runtime +will make only the PostgreSQL persistence unit and datasource available, +and setting `quarkus.hibernate-orm."oracle".active=true` and `quarkus.datasource."oracle".active=true` at runtime +will make only the Oracle persistence unit and datasource available. + +[TIP] +==== +xref:config-reference.adoc#custom-profiles[Custom configuration profiles] can help simplify such a setup. +By appending the following profile-specific configuration to the one above, +you can select a persistence unit/datasource at runtime simply by +xref:config-reference.adoc#multiple-profiles[setting `quarkus.profile`]: +`quarkus.profile=prod,pg` or `quarkus.profile=prod,oracle`. + +[source,properties] +---- +%pg.quarkus.hibernate-orm."pg".active=true +%pg.quarkus.datasource."pg".active=true +# Add any pg-related runtime configuration here, prefixed with "%pg." + +%oracle.quarkus.hibernate-orm."oracle".active=true +%oracle.quarkus.datasource."oracle".active=true +# Add any pg-related runtime configuration here, prefixed with "%pg." +---- +==== + +[TIP] +==== +It can also be useful to define a xref:cdi.adoc#ok-you-said-that-there-are-several-kinds-of-beans[CDI bean producer] redirecting to the currently active persistence unit, +like this: + +[source,java,indent=0] +---- +public class MyProducer { + @Inject + DataSourceSupport dataSourceSupport; + + @Inject + @PersistenceUnit("pg") + Session pgSessionBean; + + @Inject + @PersistenceUnit("oracle") + Session oracleSessionBean; + + @Produces + @ApplicationScoped + public Session session() { + if (dataSourceSupport.getInactiveNames().contains("pg")) { + return oracleSessionBean; + } else { + return pgSessionBean; + } + } +} +---- +==== + [[persistence-xml]] == Setting up and configuring Hibernate ORM with a `persistence.xml` 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/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..7a6d03fee425a 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,8 @@ import jakarta.annotation.PostConstruct; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; import org.eclipse.microprofile.health.HealthCheck; import org.eclipse.microprofile.health.HealthCheckResponse; @@ -16,28 +18,37 @@ 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 + Instance dataSources; + + private final Map checkedDataSources = new HashMap<>(); @PostConstruct protected void init() { - DataSourcesHealthSupport support = Arc.container().instance(DataSourcesHealthSupport.class) + if (!dataSources.isResolvable()) { + // No configured Agroal datasource at build time. + return; + } + 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.get().getDataSource(name); + if (ds != null) { + checkedDataSources.put(name, ds); } } } @@ -45,7 +56,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..58fb761d34767 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,26 @@ 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. + * + * See xref:datasource.adoc#datasource-active[this section of the documentation]. + * + * 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/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/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java index 7ba8bb8141912..a272afe7f6cac 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java @@ -17,6 +17,8 @@ public class HibernateOrmRuntimeConfigPersistenceUnit { /** * Whether this persistence unit should be active at runtime. * + * See xref:hibernate-orm.adoc#persistence-unit-active[this section of the documentation]. + * * If the persistence unit is not active, it won't start with the application, * and accessing the corresponding EntityManagerFactory/EntityManager or SessionFactory/Session * will not be possible. 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/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)) {