From 0487e7979982ba74ac9b8dc9db254867353874d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 8 Dec 2023 10:05:43 +0100 Subject: [PATCH 1/8] Fix missing space in datasource dev service logs --- .../deployment/devservices/DevServicesDatasourceProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 cccdf480f078b..22b6650110044 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 @@ -199,7 +199,7 @@ private RunningDevService startDevDb( LaunchMode launchMode, Optional consoleInstalledBuildItem, LoggingSetupBuildItem loggingSetupBuildItem, GlobalDevServicesConfig globalDevServicesConfig) { boolean explicitlyDisabled = !(dataSourceBuildTimeConfig.devservices().enabled().orElse(true)); - String dataSourcePrettyName = DataSourceUtil.isDefault(dbName) ? "default datasource" : "datasource" + dbName; + String dataSourcePrettyName = DataSourceUtil.isDefault(dbName) ? "default datasource" : "datasource " + dbName; if (explicitlyDisabled) { //explicitly disabled From 93e36f5db7828f3c7d000021faeceefd6747c5a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 22 Dec 2023 12:47:00 +0100 Subject: [PATCH 2/8] Make mutiny-based reactive SQL clients unremovable As they should be, like their non-mutiny counterpart. See https://quarkusio.zulipchat.com/#narrow/stream/187038-dev/topic/Should.20mutiny.20reactive.20clients.20be.20unremovable.3F --- .../db2/client/deployment/ReactiveDB2ClientProcessor.java | 1 + .../mssql/client/deployment/ReactiveMSSQLClientProcessor.java | 1 + .../mysql/client/deployment/ReactiveMySQLClientProcessor.java | 1 + .../oracle/client/deployment/ReactiveOracleClientProcessor.java | 1 + .../reactive/pg/client/deployment/ReactivePgClientProcessor.java | 1 + 5 files changed, 5 insertions(+) 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 d21106cfd1baf..f21ee79212fc5 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 @@ -222,6 +222,7 @@ private void createPoolIfDefined(DB2PoolRecorder recorder, .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) .createWith(recorder.mutinyDB2Pool(poolFunction)) + .unremovable() .setRuntimeInit(); addQualifiers(mutinyDB2PoolConfigurator, dataSourceName); 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 fc29eb683d158..8520590187c2d 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 @@ -221,6 +221,7 @@ private void createPoolIfDefined(MSSQLPoolRecorder recorder, .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) .createWith(recorder.mutinyMSSQLPool(poolFunction)) + .unremovable() .setRuntimeInit(); addQualifiers(mutinyMSSQLPoolConfigurator, dataSourceName); 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 7f61ab1eb9231..ab3bbadbac3a5 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 @@ -222,6 +222,7 @@ private void createPoolIfDefined(MySQLPoolRecorder recorder, .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) .createWith(recorder.mutinyMySQLPool(poolFunction)) + .unremovable() .setRuntimeInit(); addQualifiers(mutinyMySQLPoolConfigurator, dataSourceName); 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 a82812d3d8409..6eb766bf5c4a6 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 @@ -223,6 +223,7 @@ private void createPoolIfDefined(OraclePoolRecorder recorder, .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) .createWith(recorder.mutinyOraclePool(poolFunction)) + .unremovable() .setRuntimeInit(); addQualifiers(mutinyOraclePoolConfigurator, dataSourceName); 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 e1db7a21692b0..873c3b29696ee 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 @@ -227,6 +227,7 @@ private void createPoolIfDefined(PgPoolRecorder recorder, .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) .createWith(recorder.mutinyPgPool(poolFunction)) + .unremovable() .setRuntimeInit(); addQualifiers(mutinyPgPoolConfigurator, dataSourceName); From edc8e4f23e2ccbe919c745db32e1b0b32b880a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 22 Dec 2023 12:59:36 +0100 Subject: [PATCH 3/8] Enable injection of mutiny-based reactive SQL clients using type io.vertx.mutiny.sqlclient.Pool Because there's no reason not to, considering we allow similar things for non-mutiny reactive SQL clients. See also https://quarkusio.zulipchat.com/#narrow/stream/187038-dev/topic/Should.20mutiny.20reactive.20clients.20be.20unremovable.3F --- .../db2/client/deployment/ReactiveDB2ClientProcessor.java | 1 + .../mssql/client/deployment/ReactiveMSSQLClientProcessor.java | 1 + .../mysql/client/deployment/ReactiveMySQLClientProcessor.java | 1 + .../oracle/client/deployment/ReactiveOracleClientProcessor.java | 1 + .../reactive/pg/client/deployment/ReactivePgClientProcessor.java | 1 + 5 files changed, 5 insertions(+) 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 f21ee79212fc5..60187addf5680 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 @@ -219,6 +219,7 @@ private void createPoolIfDefined(DB2PoolRecorder recorder, ExtendedBeanConfigurator mutinyDB2PoolConfigurator = SyntheticBeanBuildItem .configure(io.vertx.mutiny.db2client.DB2Pool.class) .defaultBean() + .addType(io.vertx.mutiny.sqlclient.Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) .createWith(recorder.mutinyDB2Pool(poolFunction)) 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 8520590187c2d..2707e8aba1424 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 @@ -218,6 +218,7 @@ private void createPoolIfDefined(MSSQLPoolRecorder recorder, ExtendedBeanConfigurator mutinyMSSQLPoolConfigurator = SyntheticBeanBuildItem .configure(io.vertx.mutiny.mssqlclient.MSSQLPool.class) .defaultBean() + .addType(io.vertx.mutiny.sqlclient.Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) .createWith(recorder.mutinyMSSQLPool(poolFunction)) 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 ab3bbadbac3a5..4bb36e3156dd0 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 @@ -219,6 +219,7 @@ private void createPoolIfDefined(MySQLPoolRecorder recorder, ExtendedBeanConfigurator mutinyMySQLPoolConfigurator = SyntheticBeanBuildItem .configure(io.vertx.mutiny.mysqlclient.MySQLPool.class) .defaultBean() + .addType(io.vertx.mutiny.sqlclient.Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) .createWith(recorder.mutinyMySQLPool(poolFunction)) 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 6eb766bf5c4a6..ab1cf2dceff79 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 @@ -220,6 +220,7 @@ private void createPoolIfDefined(OraclePoolRecorder recorder, ExtendedBeanConfigurator mutinyOraclePoolConfigurator = SyntheticBeanBuildItem .configure(io.vertx.mutiny.oracleclient.OraclePool.class) .defaultBean() + .addType(io.vertx.mutiny.sqlclient.Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) .createWith(recorder.mutinyOraclePool(poolFunction)) 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 873c3b29696ee..cce55cfa31a5c 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 @@ -224,6 +224,7 @@ private void createPoolIfDefined(PgPoolRecorder recorder, ExtendedBeanConfigurator mutinyPgPoolConfigurator = SyntheticBeanBuildItem .configure(io.vertx.mutiny.pgclient.PgPool.class) .defaultBean() + .addType(io.vertx.mutiny.sqlclient.Pool.class) .scope(ApplicationScoped.class) .addInjectionPoint(POOL_INJECTION_TYPE, injectionPointAnnotations(dataSourceName)) .createWith(recorder.mutinyPgPool(poolFunction)) From 6de8aa0d2b766024d30cac3879f6c6538417637e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 6 Dec 2023 11:51:27 +0100 Subject: [PATCH 4/8] Test pre-existing behavior when datasources are not configured Turns out Liquibase tests were incorrect as they were setting quarkus.datasource.devservices.enabled, which means that in fact the datasource *was* configured and detected by the Agroal extension... Fixing that reveals that Liquibase will simply fail the boot when a datasource is unconfigured. --- .../io/quarkus/agroal/test/NoConfigTest.java | 81 +++++++++- ...nsionConfigEmptyDefaultDatasourceTest.java | 55 +++++++ ...tensionConfigEmptyNamedDataSourceTest.java | 41 +++++ .../test/FlywayExtensionConfigEmptyTest.java | 36 ----- ...nsionConfigMissingNamedDataSourceTest.java | 37 ----- ...StartDefaultDatasourceConfigEmptyTest.java | 37 +++++ ...AtStartNamedDatasourceConfigEmptyTest.java | 44 ++++++ ...ithExplicitUnconfiguredDatasourceTest.java | 35 ----- ...ithImplicitUnconfiguredDatasourceTest.java | 32 ---- ...ithExplicitUnconfiguredDatasourceTest.java | 34 ++++ ...ithImplicitUnconfiguredDatasourceTest.java | 33 ++++ ...ithExplicitUnconfiguredDatasourceTest.java | 34 ++++ ...ntitiesInNamedPUWithoutDatasourceTest.java | 34 ++++ ...ithExplicitUnconfiguredDatasourceTest.java | 35 ----- ...ntitiesInNamedPUWithoutDatasourceTest.java | 32 ---- ...xplicit-unconfigured-datasource.properties | 3 - ...xplicit-unconfigured-datasource.properties | 3 - ...lication-named-pu-no-datasource.properties | 4 - ...ithExplicitUnconfiguredDatasourceTest.java | 23 +++ ...ithImplicitUnconfiguredDatasourceTest.java | 23 +++ extensions/liquibase/deployment/pom.xml | 5 + ...nsionConfigEmptyDefaultDatasourceTest.java | 28 ++++ ...tensionConfigEmptyNamedDatasourceTest.java | 40 +++++ .../LiquibaseExtensionConfigEmptyTest.java | 36 ----- ...nsionConfigMissingNamedDataSourceTest.java | 38 ----- ...StartDefaultDatasourceConfigEmptyTest.java | 31 ++++ ...AtStartNamedDatasourceConfigEmptyTest.java | 44 ++++++ .../test/resources/config-empty.properties | 1 - .../reactive/mssql/client/NoConfigTest.java | 145 ++++++++++++++++++ .../reactive/mysql/client/NoConfigTest.java | 145 ++++++++++++++++++ .../reactive/oracle/client/NoConfigTest.java | 145 ++++++++++++++++++ .../reactive/pg/client/NoConfigTest.java | 144 +++++++++++++++++ 32 files changed, 1163 insertions(+), 295 deletions(-) create mode 100644 extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyDefaultDatasourceTest.java create mode 100644 extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyNamedDataSourceTest.java delete mode 100644 extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyTest.java delete mode 100644 extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigMissingNamedDataSourceTest.java create mode 100644 extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java create mode 100644 extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartNamedDatasourceConfigEmptyTest.java delete mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java delete mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInNamedPUWithoutDatasourceTest.java delete mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/namedpu/EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest.java delete mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/namedpu/EntitiesInNamedPUWithoutDatasourceTest.java delete mode 100644 extensions/hibernate-orm/deployment/src/test/resources/application-default-pu-explicit-unconfigured-datasource.properties delete mode 100644 extensions/hibernate-orm/deployment/src/test/resources/application-named-pu-explicit-unconfigured-datasource.properties delete mode 100644 extensions/hibernate-orm/deployment/src/test/resources/application-named-pu-no-datasource.properties create mode 100644 extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/datasource/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java create mode 100644 extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java create mode 100644 extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyDefaultDatasourceTest.java create mode 100644 extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyNamedDatasourceTest.java delete mode 100644 extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyTest.java delete mode 100644 extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigMissingNamedDataSourceTest.java create mode 100644 extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java create mode 100644 extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartNamedDatasourceConfigEmptyTest.java delete mode 100644 extensions/liquibase/deployment/src/test/resources/config-empty.properties create mode 100644 extensions/reactive-mssql-client/deployment/src/test/java/io/quarkus/reactive/mssql/client/NoConfigTest.java create mode 100644 extensions/reactive-mysql-client/deployment/src/test/java/io/quarkus/reactive/mysql/client/NoConfigTest.java create mode 100644 extensions/reactive-oracle-client/deployment/src/test/java/io/quarkus/reactive/oracle/client/NoConfigTest.java create mode 100644 extensions/reactive-pg-client/deployment/src/test/java/io/quarkus/reactive/pg/client/NoConfigTest.java diff --git a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/NoConfigTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/NoConfigTest.java index 9e6c4eaade823..9d8e268078bce 100644 --- a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/NoConfigTest.java +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/NoConfigTest.java @@ -1,19 +1,94 @@ 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.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; +/** + * We should be able to start the application, even with no configuration at all. + */ public class NoConfigTest { @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest(); + static final QuarkusUnitTest config = new QuarkusUnitTest() + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false"); + + @Inject + MyBean myBean; @Test - public void testNoConfig() throws SQLException { - // we should be able to start the application, even with no configuration at all + public void dataSource_default() { + DataSource ds = Arc.container().instance(DataSource.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(ds).isNotNull(); + // However, if unconfigured, any attempt to use it at runtime will fail. + assertThatThrownBy(() -> ds.getConnection()) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("quarkus.datasource.jdbc.url has not been defined"); + } + + @Test + public void agroalDataSource_default() { + AgroalDataSource ds = Arc.container().instance(AgroalDataSource.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(ds).isNotNull(); + // However, if unconfigured, any attempt to use it at runtime will fail. + assertThatThrownBy(() -> ds.getConnection()) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("quarkus.datasource.jdbc.url has not been defined"); + } + + @Test + public void dataSource_named() { + DataSource ds = Arc.container().instance(DataSource.class, + new io.quarkus.agroal.DataSource.DataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(ds).isNull(); + } + + @Test + public void agroalDataSource_named() { + AgroalDataSource ds = Arc.container().instance(AgroalDataSource.class, + new io.quarkus.agroal.DataSource.DataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(ds).isNull(); + } + + @Test + public void injectedBean_default() { + assertThatThrownBy(() -> myBean.useDataSource()) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("quarkus.datasource.jdbc.url has not been defined"); + } + + @ApplicationScoped + public static class MyBean { + @Inject + DataSource ds; + + public void useDataSource() throws SQLException { + ds.getConnection(); + } } } diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyDefaultDatasourceTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyDefaultDatasourceTest.java new file mode 100644 index 0000000000000..5b32fb0a12fcb --- /dev/null +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyDefaultDatasourceTest.java @@ -0,0 +1,55 @@ +package io.quarkus.flyway.test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.enterprise.context.ApplicationScoped; +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 FlywayExtensionConfigEmptyDefaultDatasourceTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false"); + + @Inject + Instance flywayForDefaultDatasource; + + @Inject + MyBean myBean; + + @Test + @DisplayName("If there is no config for the default datasource, the application should boot, but Flyway should be deactivated for that datasource") + public void testBootSucceedsButFlywayDeactivated() { + assertThatThrownBy(flywayForDefaultDatasource::get) + .isInstanceOf(CreationException.class) + .hasMessageContaining("Cannot get a Flyway instance for unconfigured datasource "); + } + + @Test + @DisplayName("If there is no config for the default datasource, the application should boot even if we inject a bean that depends on Liquibase, but actually using Liquibase should fail") + public void testBootSucceedsWithInjectedBeanDependingOnFlywayButFlywayDeactivated() { + assertThatThrownBy(() -> myBean.useFlyway()) + .cause() + .hasMessageContaining("Cannot get a Flyway instance for unconfigured datasource "); + } + + @ApplicationScoped + public static class MyBean { + @Inject + Flyway flywayForDefaultDatasource; + + public void useFlyway() { + flywayForDefaultDatasource.getConfiguration(); + } + } +} diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyNamedDataSourceTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyNamedDataSourceTest.java new file mode 100644 index 0000000000000..a195ce306b47c --- /dev/null +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyNamedDataSourceTest.java @@ -0,0 +1,41 @@ +package io.quarkus.flyway.test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.UnsatisfiedResolutionException; +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 FlywayExtensionConfigEmptyNamedDataSourceTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + // 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") + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false"); + + @Inject + @FlywayDataSource("users") + Instance flywayForNamedDatasource; + + @Test + @DisplayName("If there is no config for a named datasource, the application should boot, but Flyway should be deactivated for that datasource") + public void testBootSucceedsButFlywayDeactivated() { + assertThatThrownBy(flywayForNamedDatasource::get) + .isInstanceOf(UnsatisfiedResolutionException.class) + .hasMessageContaining("No bean found"); + } +} diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyTest.java deleted file mode 100644 index d5f680c6d9ef6..0000000000000 --- a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.quarkus.flyway.test; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -import jakarta.enterprise.inject.Instance; -import jakarta.enterprise.inject.UnsatisfiedResolutionException; -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; - -/** - * Flyway needs a datasource to work. - * This tests assures, that an error occurs, - * as soon as the default flyway configuration points to a missing default datasource. - */ -public class FlywayExtensionConfigEmptyTest { - - @Inject - Instance flyway; - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withApplicationRoot((jar) -> jar - .addAsResource("config-empty.properties", "application.properties")); - - @Test - @DisplayName("Injecting (default) flyway should fail if there is no datasource configured") - public void testFlywayNotAvailableWithoutDataSource() { - assertThrows(UnsatisfiedResolutionException.class, flyway::get); - } -} diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigMissingNamedDataSourceTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigMissingNamedDataSourceTest.java deleted file mode 100644 index af5c7cca818f5..0000000000000 --- a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigMissingNamedDataSourceTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.quarkus.flyway.test; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -import jakarta.enterprise.inject.Instance; -import jakarta.enterprise.inject.UnsatisfiedResolutionException; -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; - -/** - * Flyway needs a datasource to work. - * This tests assures that an error occurs as soon as a named flyway configuration points to a missing datasource. - */ -public class FlywayExtensionConfigMissingNamedDataSourceTest { - - @Inject - @FlywayDataSource("users") - Instance flyway; - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withApplicationRoot((jar) -> jar - .addAsResource("config-for-missing-named-datasource.properties", "application.properties")); - - @Test - @DisplayName("Injecting flyway should fail if the named datasource is missing") - public void testFlywayNotAvailableWithoutDataSource() { - assertThrows(UnsatisfiedResolutionException.class, flyway::get); - } -} diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java new file mode 100644 index 0000000000000..b2b024f6e83dd --- /dev/null +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java @@ -0,0 +1,37 @@ +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 FlywayExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource("db/migration/V1.0.0__Quarkus.sql")) + .overrideConfigKey("quarkus.flyway.migrate-at-start", "true") + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false"); + + @Inject + Instance flywayForDefaultDatasource; + + @Test + @DisplayName("If there is no config for the default datasource, 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) + .hasMessageContaining("Cannot get a Flyway instance for unconfigured datasource "); + } + +} diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartNamedDatasourceConfigEmptyTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartNamedDatasourceConfigEmptyTest.java new file mode 100644 index 0000000000000..48e507e40783d --- /dev/null +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartNamedDatasourceConfigEmptyTest.java @@ -0,0 +1,44 @@ +package io.quarkus.flyway.test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.UnsatisfiedResolutionException; +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 FlywayExtensionMigrateAtStartNamedDatasourceConfigEmptyTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource("db/migration/V1.0.0__Quarkus.sql")) + .overrideConfigKey("quarkus.flyway.users.migrate-at-start", "true") + // 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") + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false"); + + @Inject + @FlywayDataSource("users") + Instance flywayForNamedDatasource; + + @Test + @DisplayName("If there is no config for a named datasource, 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(UnsatisfiedResolutionException.class) + .hasMessageContaining("No bean found"); + } +} diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java deleted file mode 100644 index 4285499aad472..0000000000000 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.quarkus.hibernate.orm.config; - -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.runtime.configuration.ConfigurationException; -import io.quarkus.test.QuarkusUnitTest; - -public class EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest { - - @RegisterExtension - static QuarkusUnitTest runner = new QuarkusUnitTest() - .assertException(t -> { - assertThat(t) - .isInstanceOf(ConfigurationException.class) - .hasMessageContainingAll( - "The datasource 'ds-1' is not configured but the persistence unit '' uses it.", - "To solve this, configure datasource 'ds-1'.", - "Refer to https://quarkus.io/guides/datasource for guidance."); - }) - .withApplicationRoot((jar) -> jar - .addClass(MyEntity.class) - .addAsResource("application-default-pu-explicit-unconfigured-datasource.properties", - "application.properties")); - - @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/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java deleted file mode 100644 index 41da125a13c78..0000000000000 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.quarkus.hibernate.orm.config; - -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.runtime.configuration.ConfigurationException; -import io.quarkus.test.QuarkusUnitTest; - -public class EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest { - - @RegisterExtension - static QuarkusUnitTest runner = new QuarkusUnitTest() - .assertException(t -> { - assertThat(t) - .isInstanceOf(ConfigurationException.class) - .hasMessageContainingAll( - "Model classes are defined for the default persistence unit, but no default datasource was found. The default EntityManagerFactory will not be created. To solve this, configure the default datasource. Refer to https://quarkus.io/guides/datasource for guidance."); - }) - .withApplicationRoot((jar) -> jar - .addClass(MyEntity.class)) - .overrideConfigKey("quarkus.datasource.devservices.enabled", "false"); - - @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/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java new file mode 100644 index 0000000000000..e8d0b468572c4 --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java @@ -0,0 +1,34 @@ +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 EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest { + + @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") + .assertException(t -> assertThat(t) + .isInstanceOf(ConfigurationException.class) + .hasMessageContainingAll( + "The datasource 'ds-1' is not configured but the persistence unit '' uses it.", + "To solve this, 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/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java new file mode 100644 index 0000000000000..bfd25fe48eaaf --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java @@ -0,0 +1,33 @@ +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.test.QuarkusUnitTest; + +public class EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClass(MyEntity.class)) + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false") + .assertException(t -> assertThat(t) + .isInstanceOf(RuntimeException.class) + .hasMessageContainingAll( + "Model classes are defined for the default persistence unit but configured datasource not found", + "To solve this, configure the default 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/EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest.java new file mode 100644 index 0000000000000..5fc829242492a --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest.java @@ -0,0 +1,34 @@ +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 EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest { + + @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") + .assertException(t -> assertThat(t) + .isInstanceOf(ConfigurationException.class) + .hasMessageContainingAll( + "The datasource 'ds-1' is not configured but the persistence unit 'pu-1' uses it.", + "To solve this, 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/EntitiesInNamedPUWithoutDatasourceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInNamedPUWithoutDatasourceTest.java new file mode 100644 index 0000000000000..b2ca39823cdbb --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInNamedPUWithoutDatasourceTest.java @@ -0,0 +1,34 @@ +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 EntitiesInNamedPUWithoutDatasourceTest { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addPackage(MyEntity.class.getPackage().getName())) + // There will still be a default datasource if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false") + // We need at least one build-time property, otherwise the PU gets ignored... + .overrideConfigKey("quarkus.hibernate-orm.pu-1.packages", MyEntity.class.getPackageName()) + .overrideConfigKey("quarkus.hibernate-orm.pu-1.database.generation", "drop-and-create") + .assertException(t -> assertThat(t) + .isInstanceOf(ConfigurationException.class) + .hasMessageContainingAll("Datasource must be defined for persistence unit 'pu-1'."));; + + @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/namedpu/EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/namedpu/EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest.java deleted file mode 100644 index fdcaf43005835..0000000000000 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/namedpu/EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.quarkus.hibernate.orm.config.namedpu; - -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.runtime.configuration.ConfigurationException; -import io.quarkus.test.QuarkusUnitTest; - -public class EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest { - - @RegisterExtension - static QuarkusUnitTest runner = new QuarkusUnitTest() - .assertException(t -> { - assertThat(t) - .isInstanceOf(ConfigurationException.class) - .hasMessageContainingAll( - "The datasource 'ds-1' is not configured but the persistence unit 'pu-1' uses it.", - "To solve this, configure datasource 'ds-1'.", - "Refer to https://quarkus.io/guides/datasource for guidance."); - }) - .withApplicationRoot((jar) -> jar - .addPackage(MyEntity.class.getPackage().getName()) - .addAsResource("application-named-pu-explicit-unconfigured-datasource.properties", - "application.properties")); - - @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/namedpu/EntitiesInNamedPUWithoutDatasourceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/namedpu/EntitiesInNamedPUWithoutDatasourceTest.java deleted file mode 100644 index 552e9079dcd5b..0000000000000 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/namedpu/EntitiesInNamedPUWithoutDatasourceTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.quarkus.hibernate.orm.config.namedpu; - -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.runtime.configuration.ConfigurationException; -import io.quarkus.test.QuarkusUnitTest; - -public class EntitiesInNamedPUWithoutDatasourceTest { - - @RegisterExtension - static QuarkusUnitTest runner = new QuarkusUnitTest() - .assertException(t -> { - assertThat(t) - .isInstanceOf(ConfigurationException.class) - .hasMessageContainingAll("Datasource must be defined for persistence unit 'pu-1'."); - }) - .withConfigurationResource("application-named-pu-no-datasource.properties") - .overrideConfigKey("quarkus.datasource.devservices.enabled", "false") - .withApplicationRoot((jar) -> jar - .addPackage(MyEntity.class.getPackage().getName())); - - @Test - public void testInvalidConfiguration() { - // deployment exception should happen first - Assertions.fail(); - } - -} diff --git a/extensions/hibernate-orm/deployment/src/test/resources/application-default-pu-explicit-unconfigured-datasource.properties b/extensions/hibernate-orm/deployment/src/test/resources/application-default-pu-explicit-unconfigured-datasource.properties deleted file mode 100644 index 6bc5049280142..0000000000000 --- a/extensions/hibernate-orm/deployment/src/test/resources/application-default-pu-explicit-unconfigured-datasource.properties +++ /dev/null @@ -1,3 +0,0 @@ -quarkus.hibernate-orm.datasource=ds-1 -#quarkus.hibernate-orm.log.sql=true -quarkus.hibernate-orm.database.generation=drop-and-create diff --git a/extensions/hibernate-orm/deployment/src/test/resources/application-named-pu-explicit-unconfigured-datasource.properties b/extensions/hibernate-orm/deployment/src/test/resources/application-named-pu-explicit-unconfigured-datasource.properties deleted file mode 100644 index 4d7291dba7fcb..0000000000000 --- a/extensions/hibernate-orm/deployment/src/test/resources/application-named-pu-explicit-unconfigured-datasource.properties +++ /dev/null @@ -1,3 +0,0 @@ -quarkus.hibernate-orm.pu-1.datasource=ds-1 -quarkus.hibernate-orm.pu-1.log.sql=true -quarkus.hibernate-orm.pu-1.database.generation=drop-and-create diff --git a/extensions/hibernate-orm/deployment/src/test/resources/application-named-pu-no-datasource.properties b/extensions/hibernate-orm/deployment/src/test/resources/application-named-pu-no-datasource.properties deleted file mode 100644 index 422410bbf6fe1..0000000000000 --- a/extensions/hibernate-orm/deployment/src/test/resources/application-named-pu-no-datasource.properties +++ /dev/null @@ -1,4 +0,0 @@ -# We need at least one build-time property, otherwise the PU gets ignored... -quarkus.hibernate-orm.pu-1.packages=io.quarkus.hibernate.orm.config.namedpu -quarkus.hibernate-orm.pu-1.log.sql=true -quarkus.hibernate-orm.pu-1.database.generation=drop-and-create diff --git a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/datasource/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/datasource/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java new file mode 100644 index 0000000000000..c4c11e2307d01 --- /dev/null +++ b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/datasource/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java @@ -0,0 +1,23 @@ +package io.quarkus.hibernate.reactive.config.datasource; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.hibernate.reactive.config.MyEntity; +import io.quarkus.test.QuarkusUnitTest; + +public class EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest { + + @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"); + + @Test + public void testInvalidConfiguration() { + // bootstrap will succeed and ignore the fact that a datasource is unconfigured... + } + +} diff --git a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java new file mode 100644 index 0000000000000..74f0f25029c80 --- /dev/null +++ b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java @@ -0,0 +1,23 @@ +package io.quarkus.hibernate.reactive.config.datasource; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.hibernate.reactive.config.MyEntity; +import io.quarkus.test.QuarkusUnitTest; + +public class EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClass(MyEntity.class)) + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false"); + + @Test + public void testInvalidConfiguration() { + // bootstrap will succeed and ignore the fact that a datasource is unconfigured... + } + +} diff --git a/extensions/liquibase/deployment/pom.xml b/extensions/liquibase/deployment/pom.xml index 7a82d54cf07dd..9c3ba43b1a015 100644 --- a/extensions/liquibase/deployment/pom.xml +++ b/extensions/liquibase/deployment/pom.xml @@ -42,6 +42,11 @@ quarkus-junit5-internal test + + org.assertj + assertj-core + test + io.quarkus quarkus-test-h2 diff --git a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyDefaultDatasourceTest.java b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyDefaultDatasourceTest.java new file mode 100644 index 0000000000000..5a3350d35f812 --- /dev/null +++ b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyDefaultDatasourceTest.java @@ -0,0 +1,28 @@ +package io.quarkus.liquibase.test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; + +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 LiquibaseExtensionConfigEmptyDefaultDatasourceTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false") + .assertException(t -> assertThat(t).rootCause() + .hasMessageContaining("No datasource has been configured")); + + @Test + @DisplayName("If there is no config for the default datasource, the application should fail to boot") + public void testBootFails() { + // Should not be reached because boot should fail. + assertTrue(false); + } + +} diff --git a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyNamedDatasourceTest.java b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyNamedDatasourceTest.java new file mode 100644 index 0000000000000..8e532e29c17a0 --- /dev/null +++ b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyNamedDatasourceTest.java @@ -0,0 +1,40 @@ +package io.quarkus.liquibase.test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.UnsatisfiedResolutionException; +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 LiquibaseExtensionConfigEmptyNamedDatasourceTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false") + // 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 there is no config for a named datasource, the application should boot, but Liquibase should be deactivated for that datasource") + public void testBootSucceedsButLiquibaseDeactivated() { + assertThatThrownBy(() -> liquibaseForNamedDatasource.get().getConfiguration()) + .isInstanceOf(UnsatisfiedResolutionException.class) + .hasMessageContaining("No bean found"); + } +} diff --git a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyTest.java b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyTest.java deleted file mode 100644 index d051c5fe33ad4..0000000000000 --- a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.quarkus.liquibase.test; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -import jakarta.enterprise.inject.Instance; -import jakarta.enterprise.inject.UnsatisfiedResolutionException; -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; - -/** - * Liquibase needs a datasource to work. - * This tests assures, that an error occurs, - * as soon as the default liquibase configuration points to an missing default datasource. - */ -public class LiquibaseExtensionConfigEmptyTest { - - @Inject - Instance liquibase; - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withApplicationRoot((jar) -> jar - .addAsResource("config-empty.properties", "application.properties")); - - @Test - @DisplayName("Injecting (default) liquibase should fail if there is no datasource configured") - public void testLiquibaseNotAvailableWithoutDataSource() { - assertThrows(UnsatisfiedResolutionException.class, () -> liquibase.get().getConfiguration()); - } -} diff --git a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigMissingNamedDataSourceTest.java b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigMissingNamedDataSourceTest.java deleted file mode 100644 index 9f215776eb804..0000000000000 --- a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigMissingNamedDataSourceTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.quarkus.liquibase.test; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -import jakarta.enterprise.inject.Instance; -import jakarta.enterprise.inject.UnsatisfiedResolutionException; -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; - -/** - * Liquibase needs a datasource to work. - * This tests assures, that an error occurs, as soon as a named liquibase configuration points to a missing datasource. - */ -public class LiquibaseExtensionConfigMissingNamedDataSourceTest { - - @Inject - @LiquibaseDataSource("users") - Instance liquibase; - - @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest() - .withApplicationRoot((jar) -> jar - .addAsResource("db/changeLog.xml", "db/changeLog.xml") - .addAsResource("config-for-missing-named-datasource.properties", "application.properties")); - - @Test - @DisplayName("Injecting liquibase should fail if the named datasource is missing") - public void testLiquibaseNotAvailableWithoutDataSource() { - assertThrows(UnsatisfiedResolutionException.class, liquibase::get); - } -} diff --git a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java new file mode 100644 index 0000000000000..c6f28a7edd526 --- /dev/null +++ b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java @@ -0,0 +1,31 @@ +package io.quarkus.liquibase.test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; + +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 LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource("db/changeLog.xml", "db/changeLog.xml")) + .overrideConfigKey("quarkus.liquibase.migrate-at-start", "true") + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false") + .assertException(t -> assertThat(t).rootCause() + .hasMessageContaining("No datasource has been configured")); + + @Test + @DisplayName("If there is no config for the default datasource, and if migrate-at-start is enabled, the application should fail to boot") + public void testBootFails() { + // Should not be reached because boot should fail. + assertTrue(false); + } + +} diff --git a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartNamedDatasourceConfigEmptyTest.java b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartNamedDatasourceConfigEmptyTest.java new file mode 100644 index 0000000000000..3cc31a063195b --- /dev/null +++ b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartNamedDatasourceConfigEmptyTest.java @@ -0,0 +1,44 @@ +package io.quarkus.liquibase.test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.UnsatisfiedResolutionException; +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 LiquibaseExtensionMigrateAtStartNamedDatasourceConfigEmptyTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource("db/changeLog.xml", "db/changeLog.xml")) + .overrideConfigKey("quarkus.liquibase.users.migrate-at-start", "true") + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false") + // 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 there is no config for a named datasource, 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(UnsatisfiedResolutionException.class) + .hasMessageContaining("No bean found"); + } +} diff --git a/extensions/liquibase/deployment/src/test/resources/config-empty.properties b/extensions/liquibase/deployment/src/test/resources/config-empty.properties deleted file mode 100644 index 7484177fc8b23..0000000000000 --- a/extensions/liquibase/deployment/src/test/resources/config-empty.properties +++ /dev/null @@ -1 +0,0 @@ -quarkus.datasource.devservices.enabled=false \ No newline at end of file diff --git a/extensions/reactive-mssql-client/deployment/src/test/java/io/quarkus/reactive/mssql/client/NoConfigTest.java b/extensions/reactive-mssql-client/deployment/src/test/java/io/quarkus/reactive/mssql/client/NoConfigTest.java new file mode 100644 index 0000000000000..16b287f159e63 --- /dev/null +++ b/extensions/reactive-mssql-client/deployment/src/test/java/io/quarkus/reactive/mssql/client/NoConfigTest.java @@ -0,0 +1,145 @@ +package io.quarkus.reactive.mssql.client; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; +import java.util.concurrent.CompletionStage; + +import jakarta.enterprise.context.ApplicationScoped; +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.reactive.datasource.ReactiveDataSource; +import io.quarkus.test.QuarkusUnitTest; +import io.vertx.mssqlclient.MSSQLPool; +import io.vertx.sqlclient.Pool; + +/** + * We should be able to start the application, even with no configuration at all. + */ +public class NoConfigTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false"); + + private static final Duration MAX_WAIT = Duration.ofSeconds(10); + + @Inject + MyBean myBean; + + @Test + public void pool_default() { + Pool pool = Arc.container().instance(Pool.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().toCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @Test + public void mutinyPool_default() { + io.vertx.mutiny.sqlclient.Pool pool = Arc.container().instance(io.vertx.mutiny.sqlclient.Pool.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().subscribeAsCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @Test + public void vendorPool_default() { + MSSQLPool pool = Arc.container().instance(MSSQLPool.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().toCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @Test + public void mutinyVendorPool_default() { + io.vertx.mutiny.mssqlclient.MSSQLPool pool = Arc.container().instance(io.vertx.mutiny.mssqlclient.MSSQLPool.class) + .get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().subscribeAsCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @Test + public void pool_named() { + Pool pool = Arc.container().instance(Pool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void mutinyPool_named() { + io.vertx.mutiny.sqlclient.Pool pool = Arc.container().instance(io.vertx.mutiny.sqlclient.Pool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void vendorPool_named() { + MSSQLPool pool = Arc.container().instance(MSSQLPool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void mutinyVendorPool_named() { + io.vertx.mutiny.mssqlclient.MSSQLPool pool = Arc.container().instance(io.vertx.mutiny.mssqlclient.MSSQLPool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void injectedBean_default() { + assertThat(myBean.usePool()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @ApplicationScoped + public static class MyBean { + @Inject + MSSQLPool pool; + + public CompletionStage usePool() { + return pool.getConnection().toCompletionStage(); + } + } +} diff --git a/extensions/reactive-mysql-client/deployment/src/test/java/io/quarkus/reactive/mysql/client/NoConfigTest.java b/extensions/reactive-mysql-client/deployment/src/test/java/io/quarkus/reactive/mysql/client/NoConfigTest.java new file mode 100644 index 0000000000000..ea98c0acb5e8b --- /dev/null +++ b/extensions/reactive-mysql-client/deployment/src/test/java/io/quarkus/reactive/mysql/client/NoConfigTest.java @@ -0,0 +1,145 @@ +package io.quarkus.reactive.mysql.client; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; +import java.util.concurrent.CompletionStage; + +import jakarta.enterprise.context.ApplicationScoped; +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.reactive.datasource.ReactiveDataSource; +import io.quarkus.test.QuarkusUnitTest; +import io.vertx.mysqlclient.MySQLPool; +import io.vertx.sqlclient.Pool; + +/** + * We should be able to start the application, even with no configuration at all. + */ +public class NoConfigTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false"); + + private static final Duration MAX_WAIT = Duration.ofSeconds(10); + + @Inject + MyBean myBean; + + @Test + public void pool_default() { + Pool pool = Arc.container().instance(Pool.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().toCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @Test + public void mutinyPool_default() { + io.vertx.mutiny.sqlclient.Pool pool = Arc.container().instance(io.vertx.mutiny.sqlclient.Pool.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().subscribeAsCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @Test + public void vendorPool_default() { + MySQLPool pool = Arc.container().instance(MySQLPool.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().toCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @Test + public void mutinyVendorPool_default() { + io.vertx.mutiny.mysqlclient.MySQLPool pool = Arc.container().instance(io.vertx.mutiny.mysqlclient.MySQLPool.class) + .get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().subscribeAsCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @Test + public void pool_named() { + Pool pool = Arc.container().instance(Pool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void mutinyPool_named() { + io.vertx.mutiny.sqlclient.Pool pool = Arc.container().instance(io.vertx.mutiny.sqlclient.Pool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void vendorPool_named() { + MySQLPool pool = Arc.container().instance(MySQLPool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void mutinyVendorPool_named() { + io.vertx.mutiny.mysqlclient.MySQLPool pool = Arc.container().instance(io.vertx.mutiny.mysqlclient.MySQLPool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void injectedBean_default() { + assertThat(myBean.usePool()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @ApplicationScoped + public static class MyBean { + @Inject + MySQLPool pool; + + public CompletionStage usePool() { + return pool.getConnection().toCompletionStage(); + } + } +} diff --git a/extensions/reactive-oracle-client/deployment/src/test/java/io/quarkus/reactive/oracle/client/NoConfigTest.java b/extensions/reactive-oracle-client/deployment/src/test/java/io/quarkus/reactive/oracle/client/NoConfigTest.java new file mode 100644 index 0000000000000..7b2899780263f --- /dev/null +++ b/extensions/reactive-oracle-client/deployment/src/test/java/io/quarkus/reactive/oracle/client/NoConfigTest.java @@ -0,0 +1,145 @@ +package io.quarkus.reactive.oracle.client; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; +import java.util.concurrent.CompletionStage; + +import jakarta.enterprise.context.ApplicationScoped; +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.reactive.datasource.ReactiveDataSource; +import io.quarkus.test.QuarkusUnitTest; +import io.vertx.oracleclient.OraclePool; +import io.vertx.sqlclient.Pool; + +/** + * We should be able to start the application, even with no configuration at all. + */ +public class NoConfigTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false"); + + private static final Duration MAX_WAIT = Duration.ofSeconds(10); + + @Inject + MyBean myBean; + + @Test + public void pool_default() { + Pool pool = Arc.container().instance(Pool.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().toCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Cannot connect"); + } + + @Test + public void mutinyPool_default() { + io.vertx.mutiny.sqlclient.Pool pool = Arc.container().instance(io.vertx.mutiny.sqlclient.Pool.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().subscribeAsCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Cannot connect"); + } + + @Test + public void vendorPool_default() { + OraclePool pool = Arc.container().instance(OraclePool.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().toCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Cannot connect"); + } + + @Test + public void mutinyVendorPool_default() { + io.vertx.mutiny.oracleclient.OraclePool pool = Arc.container().instance(io.vertx.mutiny.oracleclient.OraclePool.class) + .get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().subscribeAsCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Cannot connect"); + } + + @Test + public void pool_named() { + Pool pool = Arc.container().instance(Pool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void mutinyPool_named() { + io.vertx.mutiny.sqlclient.Pool pool = Arc.container().instance(io.vertx.mutiny.sqlclient.Pool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void vendorPool_named() { + OraclePool pool = Arc.container().instance(OraclePool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void mutinyVendorPool_named() { + io.vertx.mutiny.oracleclient.OraclePool pool = Arc.container().instance(io.vertx.mutiny.oracleclient.OraclePool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void injectedBean_default() { + assertThat(myBean.usePool()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Cannot connect"); + } + + @ApplicationScoped + public static class MyBean { + @Inject + OraclePool pool; + + public CompletionStage usePool() { + return pool.getConnection().toCompletionStage(); + } + } +} diff --git a/extensions/reactive-pg-client/deployment/src/test/java/io/quarkus/reactive/pg/client/NoConfigTest.java b/extensions/reactive-pg-client/deployment/src/test/java/io/quarkus/reactive/pg/client/NoConfigTest.java new file mode 100644 index 0000000000000..ceaa86c73563f --- /dev/null +++ b/extensions/reactive-pg-client/deployment/src/test/java/io/quarkus/reactive/pg/client/NoConfigTest.java @@ -0,0 +1,144 @@ +package io.quarkus.reactive.pg.client; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; +import java.util.concurrent.CompletionStage; + +import jakarta.enterprise.context.ApplicationScoped; +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.reactive.datasource.ReactiveDataSource; +import io.quarkus.test.QuarkusUnitTest; +import io.vertx.pgclient.PgPool; +import io.vertx.sqlclient.Pool; + +/** + * We should be able to start the application, even with no configuration at all. + */ +public class NoConfigTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + // The datasource won't be truly "unconfigured" if dev services are enabled + .overrideConfigKey("quarkus.devservices.enabled", "false"); + + private static final Duration MAX_WAIT = Duration.ofSeconds(10); + + @Inject + MyBean myBean; + + @Test + public void pool_default() { + Pool pool = Arc.container().instance(Pool.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().toCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @Test + public void mutinyPool_default() { + io.vertx.mutiny.sqlclient.Pool pool = Arc.container().instance(io.vertx.mutiny.sqlclient.Pool.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().subscribeAsCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @Test + public void vendorPool_default() { + PgPool pool = Arc.container().instance(PgPool.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().toCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @Test + public void mutinyVendorPool_default() { + io.vertx.mutiny.pgclient.PgPool pool = Arc.container().instance(io.vertx.mutiny.pgclient.PgPool.class).get(); + + // The default datasource is a bit special; + // it's historically always been considered as "present" even if there was no explicit configuration. + // So the bean will never be null. + assertThat(pool).isNotNull(); + // However, if unconfigured, it will use default connection config (host, port, username, ...) and will fail. + assertThat(pool.getConnection().subscribeAsCompletionStage()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @Test + public void pool_named() { + Pool pool = Arc.container().instance(Pool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void mutinyPool_named() { + io.vertx.mutiny.sqlclient.Pool pool = Arc.container().instance(io.vertx.mutiny.sqlclient.Pool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void vendorPool_named() { + PgPool pool = Arc.container().instance(PgPool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void mutinyVendorPool_named() { + io.vertx.mutiny.pgclient.PgPool pool = Arc.container().instance(io.vertx.mutiny.pgclient.PgPool.class, + new ReactiveDataSource.ReactiveDataSourceLiteral("ds-1")).get(); + // An unconfigured, named datasource has no corresponding bean. + assertThat(pool).isNull(); + } + + @Test + public void injectedBean_default() { + assertThat(myBean.usePool()) + .failsWithin(MAX_WAIT) + .withThrowableThat() + .withMessageContaining("Connection refused"); + } + + @ApplicationScoped + public static class MyBean { + @Inject + PgPool pool; + + public CompletionStage usePool() { + return pool.getConnection().toCompletionStage(); + } + } +} From c4dcbf3752e1c86be8164b8678daac9ef82bcf63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 6 Dec 2023 16:29:24 +0100 Subject: [PATCH 5/8] Make DataSource beans application-scoped So that we'll be able to postpone initialization to first access in some cases, instead of doing it on startup. This could be useful in particular for deactivated datasources: we don't want to initialize those on startup, but we do want them to fail on first use. An alternative would have been to represent deactivated datasources with a custom implementation of AgroalDataSource, like we currently do with UnconfiguredDataSource, but that solution has serious problems, in particular when we "forget" to implement some methods: see https://github.com/quarkusio/quarkus/issues/36666 --- .../agroal/deployment/AgroalProcessor.java | 6 ++- .../quarkus/agroal/test/EagerStartupTest.java | 41 +++++++++++++++++++ .../quarkus/agroal/runtime/DataSources.java | 12 ++++++ ...ithImplicitUnconfiguredDatasourceTest.java | 2 +- .../FastBootHibernatePersistenceProvider.java | 32 ++++++--------- 5 files changed, 70 insertions(+), 23 deletions(-) create mode 100644 extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/EagerStartupTest.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 f7a7040fe886d..b52cb438b06dc 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 @@ -14,8 +14,8 @@ import javax.sql.XADataSource; +import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Default; -import jakarta.inject.Singleton; import org.jboss.jandex.ClassType; import org.jboss.jandex.DotName; @@ -72,6 +72,7 @@ class AgroalProcessor { private static final String OPEN_TELEMETRY_DRIVER = "io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver"; private static final DotName DATA_SOURCE = DotName.createSimple(javax.sql.DataSource.class.getName()); + private static final DotName AGROAL_DATA_SOURCE = DotName.createSimple(AgroalDataSource.class.getName()); @BuildStep void agroal(BuildProducer feature) { @@ -277,7 +278,8 @@ void generateDataSourceBeans(AgroalRecorder recorder, SyntheticBeanBuildItem.ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem .configure(AgroalDataSource.class) .addType(DATA_SOURCE) - .scope(Singleton.class) + .addType(AGROAL_DATA_SOURCE) + .scope(ApplicationScoped.class) .setRuntimeInit() .unremovable() .addInjectionPoint(ClassType.create(DotName.createSimple(DataSources.class))) diff --git a/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/EagerStartupTest.java b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/EagerStartupTest.java new file mode 100644 index 0000000000000..838c8ad9a7131 --- /dev/null +++ b/extensions/agroal/deployment/src/test/java/io/quarkus/agroal/test/EagerStartupTest.java @@ -0,0 +1,41 @@ +package io.quarkus.agroal.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import jakarta.inject.Singleton; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.agroal.runtime.DataSources; +import io.quarkus.arc.Arc; +import io.quarkus.datasource.common.runtime.DataSourceUtil; +import io.quarkus.test.QuarkusUnitTest; + +/** + * Check that datasources are created eagerly on application startup. + *

+ * This has always been the case historically, so we want to keep it that way. + */ +public class EagerStartupTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withConfigurationResource("base.properties"); + + @Test + public void shouldStartEagerly() { + var container = Arc.container(); + var instanceHandle = container.instance(DataSources.class); + // Check that the following call won't trigger a lazy initialization: + // the DataSources bean must be eagerly initialized. + assertThat(container.getActiveContext(Singleton.class).getState() + .getContextualInstances().get(instanceHandle.getBean())) + .as("Eagerly instantiated DataSources bean") + .isNotNull(); + // Check that the datasource has already been eagerly created. + assertThat(instanceHandle.get().isDataSourceCreated(DataSourceUtil.DEFAULT_DATASOURCE_NAME)) + .isTrue(); + } + +} diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSources.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSources.java index 341e2cca19966..9f7f77b88326e 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 @@ -13,6 +13,7 @@ import java.util.function.Function; import java.util.stream.Collectors; +import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import jakarta.enterprise.inject.Any; import jakarta.enterprise.inject.Default; @@ -122,6 +123,10 @@ public static AgroalDataSource fromName(String dataSourceName) { .getDataSource(dataSourceName); } + public boolean isDataSourceCreated(String dataSourceName) { + return dataSources.containsKey(dataSourceName); + } + public AgroalDataSource getDataSource(String dataSourceName) { return dataSources.computeIfAbsent(dataSourceName, new Function() { @Override @@ -131,6 +136,13 @@ public AgroalDataSource apply(String s) { }); } + @PostConstruct + public void start() { + for (String dataSourceName : dataSourceSupport.entries.keySet()) { + getDataSource(dataSourceName); + } + } + @SuppressWarnings("resource") public AgroalDataSource doCreateDataSource(String dataSourceName) { if (!dataSourceSupport.entries.containsKey(dataSourceName)) { diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java index bfd25fe48eaaf..5f3baf8404b51 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java @@ -20,7 +20,7 @@ public class EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest { .assertException(t -> assertThat(t) .isInstanceOf(RuntimeException.class) .hasMessageContainingAll( - "Model classes are defined for the default persistence unit but configured datasource not found", + "Model classes are defined for persistence unit but configured datasource not found", "To solve this, configure the default datasource.", "Refer to https://quarkus.io/guides/datasource for guidance.")); diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java index 5db72232952c5..3a4048d252485 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java @@ -24,11 +24,9 @@ import org.hibernate.service.internal.ProvidedService; import org.jboss.logging.Logger; -import io.quarkus.agroal.DataSource.DataSourceLiteral; +import io.quarkus.agroal.runtime.DataSources; import io.quarkus.agroal.runtime.UnconfiguredDataSource; import io.quarkus.arc.Arc; -import io.quarkus.arc.InstanceHandle; -import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.hibernate.orm.runtime.RuntimeSettings.Builder; import io.quarkus.hibernate.orm.runtime.boot.FastBootEntityManagerFactoryBuilder; import io.quarkus.hibernate.orm.runtime.boot.RuntimePersistenceUnitDescriptor; @@ -38,7 +36,6 @@ import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; import io.quarkus.hibernate.orm.runtime.recording.PrevalidatedQuarkusMetadata; import io.quarkus.hibernate.orm.runtime.recording.RecordedState; -import io.quarkus.runtime.configuration.ConfigurationException; /** * This can not inherit from HibernatePersistenceProvider as that would force @@ -375,7 +372,7 @@ private void verifyProperties(Map properties) { } } - private static void injectDataSource(String persistenceUnitName, String dataSource, + private static void injectDataSource(String persistenceUnitName, String dataSourceName, RuntimeSettings.Builder runtimeSettingsBuilder) { // first convert @@ -389,26 +386,21 @@ private static void injectDataSource(String persistenceUnitName, String dataSour return; } - InstanceHandle dataSourceHandle; - if (DataSourceUtil.isDefault(dataSource)) { - dataSourceHandle = Arc.container().instance(DataSource.class); - } else { - dataSourceHandle = Arc.container().instance(DataSource.class, new DataSourceLiteral(dataSource)); - } - - if (!dataSourceHandle.isAvailable()) { + DataSource dataSource; + try { + dataSource = Arc.container().instance(DataSources.class).get().getDataSource(dataSourceName); + } catch (IllegalArgumentException e) { throw new IllegalStateException( - "No datasource " + dataSource + " has been defined for persistence unit " + persistenceUnitName); + "No datasource " + dataSourceName + " has been defined for persistence unit " + persistenceUnitName); } - DataSource ds = dataSourceHandle.get(); - if (ds instanceof UnconfiguredDataSource) { - throw new ConfigurationException( - "Model classes are defined for the default persistence unit " + persistenceUnitName - + " but configured datasource " + dataSource + if (dataSource instanceof UnconfiguredDataSource) { + throw new IllegalStateException( + "Model classes are defined for persistence unit " + persistenceUnitName + + " but configured datasource " + dataSourceName + " not found: the default EntityManagerFactory will not be created. To solve this, configure the default datasource. Refer to https://quarkus.io/guides/datasource for guidance."); } - runtimeSettingsBuilder.put(AvailableSettings.DATASOURCE, ds); + runtimeSettingsBuilder.put(AvailableSettings.DATASOURCE, dataSource); } private static void injectRuntimeConfiguration(HibernateOrmRuntimeConfigPersistenceUnit persistenceUnitConfig, From c0ccfa5fc7682fa178bca8303194f3b7f82e6609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Thu, 23 Nov 2023 14:51:29 +0100 Subject: [PATCH 6/8] Make sure a ConfigurationException caused by another will forward the relevant config keys We're going to use this in interactions between Hibernate ORM and Agroal datasources. --- .../configuration/ConfigurationException.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigurationException.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigurationException.java index a020bedb02852..d9042f9ee9173 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigurationException.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigurationException.java @@ -1,6 +1,7 @@ package io.quarkus.runtime.configuration; import java.util.Collections; +import java.util.HashSet; import java.util.Set; import io.quarkus.dev.config.ConfigurationProblem; @@ -55,7 +56,7 @@ public ConfigurationException(final String msg, Set configKeys) { */ public ConfigurationException(final Throwable cause, Set configKeys) { super(cause); - this.configKeys = configKeys; + this.configKeys = forwardCauseConfigKeys(configKeys, cause); } /** @@ -77,7 +78,7 @@ public ConfigurationException(final String msg, final Throwable cause) { */ public ConfigurationException(final String msg, final Throwable cause, Set configKeys) { super(msg, cause); - this.configKeys = configKeys; + this.configKeys = forwardCauseConfigKeys(configKeys, cause); } public ConfigurationException(Throwable cause) { @@ -88,4 +89,12 @@ public ConfigurationException(Throwable cause) { public Set getConfigKeys() { return configKeys; } + + private static Set forwardCauseConfigKeys(Set configKeys, Throwable cause) { + if (cause instanceof ConfigurationProblem) { + var merged = new HashSet(configKeys); + merged.addAll(((ConfigurationProblem) cause).getConfigKeys()); + } + return configKeys; + } } From c857b76938b419aeefb6f6e52748d23b11113e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Thu, 23 Nov 2023 15:35:10 +0100 Subject: [PATCH 7/8] Avoid unnecessary exception wrapping in JPAConfig 1. To preserve the type of the exception, since we react differently to ConfigurationException for example. 2. To make testing cleaner. --- .../main/java/io/quarkus/hibernate/orm/runtime/JPAConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/JPAConfig.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/JPAConfig.java index 481249f84ab5b..f7c05f7530e44 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/JPAConfig.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/JPAConfig.java @@ -75,7 +75,8 @@ public void run() { } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { - throw new RuntimeException(e.getCause()); + throw e.getCause() instanceof RuntimeException ? (RuntimeException) e.getCause() + : new RuntimeException(e.getCause()); } } } From 989ce28f7c6cf9c345ad0cc9d329337d85a2ca01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 22 Nov 2023 15:03:00 +0100 Subject: [PATCH 8/8] More consistent exceptions for unconfigured datasources Critically, this opens the way to handling other datasource access problems, e.g. deactivated datasources. --- .../quarkus/agroal/runtime/DataSources.java | 1 + extensions/datasource/common/pom.xml | 4 ++++ .../common/runtime/DataSourceUtil.java | 16 +++++++++++++ ...nsionConfigEmptyDefaultDatasourceTest.java | 11 +++++++-- ...StartDefaultDatasourceConfigEmptyTest.java | 6 ++++- .../flyway/runtime/FlywayRecorder.java | 23 ++++++++++++++----- ...UnconfiguredDataSourceFlywayContainer.java | 10 +++++--- .../orm/deployment/HibernateOrmProcessor.java | 21 +++++++---------- ...ithExplicitUnconfiguredDatasourceTest.java | 3 ++- ...ithImplicitUnconfiguredDatasourceTest.java | 8 ++++--- ...ithExplicitUnconfiguredDatasourceTest.java | 3 ++- .../FastBootHibernatePersistenceProvider.java | 16 +++++-------- .../orm/runtime/PersistenceUnitUtil.java | 10 ++++++++ ...nsionConfigEmptyDefaultDatasourceTest.java | 7 ++++-- ...StartDefaultDatasourceConfigEmptyTest.java | 7 ++++-- .../liquibase/runtime/LiquibaseRecorder.java | 15 +++++++++--- 16 files changed, 114 insertions(+), 47 deletions(-) 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 9f7f77b88326e..0e6e827b06a42 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 @@ -152,6 +152,7 @@ public AgroalDataSource doCreateDataSource(String dataSourceName) { DataSourceJdbcBuildTimeConfig dataSourceJdbcBuildTimeConfig = dataSourcesJdbcBuildTimeConfig .dataSources().get(dataSourceName).jdbc(); DataSourceRuntimeConfig dataSourceRuntimeConfig = dataSourcesRuntimeConfig.dataSources().get(dataSourceName); + DataSourceJdbcRuntimeConfig dataSourceJdbcRuntimeConfig = dataSourcesJdbcRuntimeConfig .getDataSourceJdbcRuntimeConfig(dataSourceName); diff --git a/extensions/datasource/common/pom.xml b/extensions/datasource/common/pom.xml index 6527c853e4c84..acdedbcd44a0c 100644 --- a/extensions/datasource/common/pom.xml +++ b/extensions/datasource/common/pom.xml @@ -12,6 +12,10 @@ quarkus-datasource-common Quarkus - Datasource - Common + + io.quarkus + quarkus-core + org.junit.jupiter junit-jupiter 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 f0a4b3378f1ba..7b11b9e4aab7a 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 @@ -2,6 +2,10 @@ import java.util.Collection; import java.util.List; +import java.util.Locale; +import java.util.Set; + +import io.quarkus.runtime.configuration.ConfigurationException; public final class DataSourceUtil { @@ -34,6 +38,18 @@ public static List dataSourcePropertyKeys(String datasourceName, String } } + public static ConfigurationException dataSourceNotConfigured(String dataSourceName) { + return new ConfigurationException(String.format(Locale.ROOT, + "Datasource '%s' is not configured." + + " To solve this, configure datasource '%s'." + + " Refer to https://quarkus.io/guides/datasource for guidance.", + dataSourceName, dataSourceName), + Set.of(dataSourcePropertyKey(dataSourceName, "db-kind"), + dataSourcePropertyKey(dataSourceName, "username"), + dataSourcePropertyKey(dataSourceName, "password"), + dataSourcePropertyKey(dataSourceName, "jdbc.url"))); + } + private DataSourceUtil() { } diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyDefaultDatasourceTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyDefaultDatasourceTest.java index 5b32fb0a12fcb..c172cdecb9a73 100644 --- a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyDefaultDatasourceTest.java +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionConfigEmptyDefaultDatasourceTest.java @@ -32,7 +32,11 @@ public class FlywayExtensionConfigEmptyDefaultDatasourceTest { public void testBootSucceedsButFlywayDeactivated() { assertThatThrownBy(flywayForDefaultDatasource::get) .isInstanceOf(CreationException.class) - .hasMessageContaining("Cannot get a Flyway instance for unconfigured datasource "); + .cause() + .hasMessageContainingAll("Unable to find datasource '' for Flyway", + "Datasource '' is not configured.", + "To solve this, configure datasource ''.", + "Refer to https://quarkus.io/guides/datasource for guidance."); } @Test @@ -40,7 +44,10 @@ public void testBootSucceedsButFlywayDeactivated() { public void testBootSucceedsWithInjectedBeanDependingOnFlywayButFlywayDeactivated() { assertThatThrownBy(() -> myBean.useFlyway()) .cause() - .hasMessageContaining("Cannot get a Flyway instance for unconfigured datasource "); + .hasMessageContainingAll("Unable to find datasource '' for Flyway", + "Datasource '' is not configured.", + "To solve this, configure datasource ''.", + "Refer to https://quarkus.io/guides/datasource for guidance."); } @ApplicationScoped diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java index b2b024f6e83dd..0f9f506d16d0a 100644 --- a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java @@ -31,7 +31,11 @@ public class FlywayExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest { public void testBootSucceedsButFlywayDeactivated() { assertThatThrownBy(flywayForDefaultDatasource::get) .isInstanceOf(CreationException.class) - .hasMessageContaining("Cannot get a Flyway instance for unconfigured datasource "); + .cause() + .hasMessageContainingAll("Unable to find datasource '' for Flyway", + "Datasource '' is not configured.", + "To solve this, configure datasource ''.", + "Refer to https://quarkus.io/guides/datasource for guidance."); } } 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 f8df08e4e7825..4e858dcf71a3b 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 @@ -2,6 +2,7 @@ import java.lang.annotation.Annotation; import java.util.Collection; +import java.util.Locale; import java.util.Map; import java.util.function.Function; @@ -31,6 +32,7 @@ import io.quarkus.flyway.FlywayDataSource.FlywayDataSourceLiteral; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.annotations.Recorder; +import io.quarkus.runtime.configuration.ConfigurationException; @Recorder public class FlywayRecorder { @@ -64,15 +66,24 @@ public Function, FlywayContainer> fl return new Function<>() { @Override public FlywayContainer apply(SyntheticCreationalContext context) { - DataSource dataSource = context.getInjectedReference(DataSources.class).getDataSource(dataSourceName); - if (dataSource instanceof UnconfiguredDataSource) { - return new UnconfiguredDataSourceFlywayContainer(dataSourceName); + DataSource dataSource; + try { + dataSource = context.getInjectedReference(DataSources.class).getDataSource(dataSourceName); + if (dataSource instanceof UnconfiguredDataSource) { + throw DataSourceUtil.dataSourceNotConfigured(dataSourceName); + } + } catch (ConfigurationException e) { + // TODO do we really want to enable retrieval of a FlywayContainer for an unconfigured datasource? + // Assigning ApplicationScoped to the FlywayContainer + // and throwing UnsatisfiedResolutionException on bean creation (first access) + // would probably make more sense. + return new UnconfiguredDataSourceFlywayContainer(dataSourceName, String.format(Locale.ROOT, + "Unable to find datasource '%s' for Flyway: %s", + dataSourceName, e.getMessage()), e); } FlywayContainerProducer flywayProducer = context.getInjectedReference(FlywayContainerProducer.class); - FlywayContainer flywayContainer = flywayProducer.createFlyway(dataSource, dataSourceName, hasMigrations, - createPossible); - return flywayContainer; + return flywayProducer.createFlyway(dataSource, dataSourceName, hasMigrations, createPossible); } }; } diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/UnconfiguredDataSourceFlywayContainer.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/UnconfiguredDataSourceFlywayContainer.java index a3206cd8141ae..5011c9898ce0d 100644 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/UnconfiguredDataSourceFlywayContainer.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/UnconfiguredDataSourceFlywayContainer.java @@ -4,13 +4,17 @@ public class UnconfiguredDataSourceFlywayContainer extends FlywayContainer { - public UnconfiguredDataSourceFlywayContainer(String dataSourceName) { + private final String message; + private final Throwable cause; + + public UnconfiguredDataSourceFlywayContainer(String dataSourceName, String message, Throwable cause) { super(null, false, false, false, false, false, dataSourceName, false, false); + this.message = message; + this.cause = cause; } @Override public Flyway getFlyway() { - throw new UnsupportedOperationException( - "Cannot get a Flyway instance for unconfigured datasource " + getDataSourceName()); + throw new UnsupportedOperationException(message, cause); } } diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java index 7c90798d1ade0..2ad2bb9e78ac7 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java @@ -871,13 +871,10 @@ private void handleHibernateORMWithNoPersistenceXml( && (!hibernateOrmConfig.defaultPersistenceUnit.datasource.isPresent() || DataSourceUtil.isDefault(hibernateOrmConfig.defaultPersistenceUnit.datasource.get())) && !defaultJdbcDataSource.isPresent()) { - throw new ConfigurationException( - "Model classes are defined for the default persistence unit, but no default datasource was found." - + " The default EntityManagerFactory will not be created." - + " To solve this, configure the default datasource." - + " Refer to https://quarkus.io/guides/datasource for guidance.", - new HashSet<>(Arrays.asList("quarkus.datasource.db-kind", "quarkus.datasource.username", - "quarkus.datasource.password", "quarkus.datasource.jdbc.url"))); + String persistenceUnitName = PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME; + String dataSourceName = DataSourceUtil.DEFAULT_DATASOURCE_NAME; + throw PersistenceUnitUtil.unableToFindDataSource(persistenceUnitName, dataSourceName, + DataSourceUtil.dataSourceNotConfigured(dataSourceName)); } for (Entry persistenceUnitEntry : hibernateOrmConfig.persistenceUnits @@ -1228,14 +1225,12 @@ private static void collectDialectConfigForPersistenceXml(String persistenceUnit private static Optional findJdbcDataSource(String persistenceUnitName, HibernateOrmConfigPersistenceUnit persistenceUnitConfig, List jdbcDataSources) { if (persistenceUnitConfig.datasource.isPresent()) { + String dataSourceName = persistenceUnitConfig.datasource.get(); return Optional.of(jdbcDataSources.stream() - .filter(i -> persistenceUnitConfig.datasource.get().equals(i.getName())) + .filter(i -> dataSourceName.equals(i.getName())) .findFirst() - .orElseThrow(() -> new ConfigurationException(String.format(Locale.ROOT, - "The datasource '%1$s' is not configured but the persistence unit '%2$s' uses it." - + " To solve this, configure datasource '%1$s'." - + " Refer to https://quarkus.io/guides/datasource for guidance.", - persistenceUnitConfig.datasource.get(), persistenceUnitName)))); + .orElseThrow(() -> PersistenceUnitUtil.unableToFindDataSource(persistenceUnitName, dataSourceName, + DataSourceUtil.dataSourceNotConfigured(dataSourceName)))); } else if (PersistenceUnitUtil.isDefaultPersistenceUnit(persistenceUnitName)) { return jdbcDataSources.stream() .filter(i -> i.isDefault()) diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java index e8d0b468572c4..95da175428278 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest.java @@ -21,7 +21,8 @@ public class EntitiesInDefaultPUWithExplicitUnconfiguredDatasourceTest { .assertException(t -> assertThat(t) .isInstanceOf(ConfigurationException.class) .hasMessageContainingAll( - "The datasource 'ds-1' is not configured but the persistence unit '' uses it.", + "Unable to find datasource 'ds-1' for persistence unit ''", + "Datasource 'ds-1' is not configured.", "To solve this, configure datasource 'ds-1'.", "Refer to https://quarkus.io/guides/datasource for guidance.")); diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java index 5f3baf8404b51..5e301b02be941 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest.java @@ -7,6 +7,7 @@ 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 EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest { @@ -18,10 +19,11 @@ public class EntitiesInDefaultPUWithImplicitUnconfiguredDatasourceTest { // The datasource won't be truly "unconfigured" if dev services are enabled .overrideConfigKey("quarkus.devservices.enabled", "false") .assertException(t -> assertThat(t) - .isInstanceOf(RuntimeException.class) + .isInstanceOf(ConfigurationException.class) .hasMessageContainingAll( - "Model classes are defined for persistence unit but configured datasource not found", - "To solve this, configure the default datasource.", + "Unable to find datasource '' for persistence unit ''", + "Datasource '' is not configured.", + "To solve this, configure datasource ''.", "Refer to https://quarkus.io/guides/datasource for guidance.")); @Test diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest.java index 5fc829242492a..bc7f76483c09a 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/datasource/EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest.java @@ -21,7 +21,8 @@ public class EntitiesInNamedPUWithExplicitUnconfiguredDatasourceTest { .assertException(t -> assertThat(t) .isInstanceOf(ConfigurationException.class) .hasMessageContainingAll( - "The datasource 'ds-1' is not configured but the persistence unit 'pu-1' uses it.", + "Unable to find datasource 'ds-1' for persistence unit 'pu-1'", + "Datasource 'ds-1' is not configured.", "To solve this, configure datasource 'ds-1'.", "Refer to https://quarkus.io/guides/datasource for guidance.")); diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java index 3a4048d252485..f98ba87694899 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java @@ -27,6 +27,7 @@ import io.quarkus.agroal.runtime.DataSources; import io.quarkus.agroal.runtime.UnconfiguredDataSource; import io.quarkus.arc.Arc; +import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.hibernate.orm.runtime.RuntimeSettings.Builder; import io.quarkus.hibernate.orm.runtime.boot.FastBootEntityManagerFactoryBuilder; import io.quarkus.hibernate.orm.runtime.boot.RuntimePersistenceUnitDescriptor; @@ -389,16 +390,11 @@ private static void injectDataSource(String persistenceUnitName, String dataSour DataSource dataSource; try { dataSource = Arc.container().instance(DataSources.class).get().getDataSource(dataSourceName); - } catch (IllegalArgumentException e) { - throw new IllegalStateException( - "No datasource " + dataSourceName + " has been defined for persistence unit " + persistenceUnitName); - } - - if (dataSource instanceof UnconfiguredDataSource) { - throw new IllegalStateException( - "Model classes are defined for persistence unit " + persistenceUnitName - + " but configured datasource " + dataSourceName - + " not found: the default EntityManagerFactory will not be created. To solve this, configure the default datasource. Refer to https://quarkus.io/guides/datasource for guidance."); + if (dataSource instanceof UnconfiguredDataSource) { + throw DataSourceUtil.dataSourceNotConfigured(dataSourceName); + } + } catch (RuntimeException e) { + throw PersistenceUnitUtil.unableToFindDataSource(persistenceUnitName, dataSourceName, e); } runtimeSettingsBuilder.put(AvailableSettings.DATASOURCE, dataSource); } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceUnitUtil.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceUnitUtil.java index b91894c052e68..a3194b02f77ad 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceUnitUtil.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceUnitUtil.java @@ -13,6 +13,7 @@ import io.quarkus.arc.InjectableInstance; import io.quarkus.hibernate.orm.PersistenceUnit; import io.quarkus.hibernate.orm.PersistenceUnitExtension; +import io.quarkus.runtime.configuration.ConfigurationException; public class PersistenceUnitUtil { private static final Logger LOG = Logger.getLogger(PersistenceUnitUtil.class); @@ -104,4 +105,13 @@ public static InjectableInstance legacySingleExtensionInstanceForPersiste private static boolean isDefaultBean(InjectableInstance instance) { return instance.isResolvable() && instance.getHandle().getBean().isDefaultBean(); } + + public static ConfigurationException unableToFindDataSource(String persistenceUnitName, + String dataSourceName, + Throwable cause) { + return new ConfigurationException(String.format(Locale.ROOT, + "Unable to find datasource '%s' for persistence unit '%s': %s", + dataSourceName, persistenceUnitName, cause.getMessage()), + cause); + } } diff --git a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyDefaultDatasourceTest.java b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyDefaultDatasourceTest.java index 5a3350d35f812..ac28eb20acf10 100644 --- a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyDefaultDatasourceTest.java +++ b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionConfigEmptyDefaultDatasourceTest.java @@ -15,8 +15,11 @@ public class LiquibaseExtensionConfigEmptyDefaultDatasourceTest { static final QuarkusUnitTest config = new QuarkusUnitTest() // The datasource won't be truly "unconfigured" if dev services are enabled .overrideConfigKey("quarkus.devservices.enabled", "false") - .assertException(t -> assertThat(t).rootCause() - .hasMessageContaining("No datasource has been configured")); + .assertException(t -> assertThat(t).cause().cause() + .hasMessageContainingAll("Unable to find datasource '' for Liquibase", + "Datasource '' is not configured.", + "To solve this, configure datasource ''.", + "Refer to https://quarkus.io/guides/datasource for guidance.")); @Test @DisplayName("If there is no config for the default datasource, the application should fail to boot") diff --git a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java index c6f28a7edd526..bad255b85ccf3 100644 --- a/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java +++ b/extensions/liquibase/deployment/src/test/java/io/quarkus/liquibase/test/LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest.java @@ -18,8 +18,11 @@ public class LiquibaseExtensionMigrateAtStartDefaultDatasourceConfigEmptyTest { .overrideConfigKey("quarkus.liquibase.migrate-at-start", "true") // The datasource won't be truly "unconfigured" if dev services are enabled .overrideConfigKey("quarkus.devservices.enabled", "false") - .assertException(t -> assertThat(t).rootCause() - .hasMessageContaining("No datasource has been configured")); + .assertException(t -> assertThat(t).cause().cause() + .hasMessageContainingAll("Unable to find datasource '' for Liquibase", + "Datasource '' is not configured.", + "To solve this, configure datasource ''.", + "Refer to https://quarkus.io/guides/datasource for guidance.")); @Test @DisplayName("If there is no config for the default datasource, and if migrate-at-start is enabled, the application should fail to boot") 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 d85785d61e35b..4b045a357417b 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 @@ -1,5 +1,6 @@ package io.quarkus.liquibase.runtime; +import java.util.Locale; import java.util.function.Function; import javax.sql.DataSource; @@ -13,6 +14,7 @@ import io.quarkus.arc.InjectableInstance; import io.quarkus.arc.InstanceHandle; import io.quarkus.arc.SyntheticCreationalContext; +import io.quarkus.datasource.common.runtime.DataSourceUtil; import io.quarkus.liquibase.LiquibaseFactory; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.annotations.Recorder; @@ -32,9 +34,16 @@ public Function, LiquibaseFactory> return new Function, LiquibaseFactory>() { @Override public LiquibaseFactory apply(SyntheticCreationalContext context) { - DataSource dataSource = context.getInjectedReference(DataSources.class).getDataSource(dataSourceName); - if (dataSource instanceof UnconfiguredDataSource) { - throw new UnsatisfiedResolutionException("No datasource has been configured"); + DataSource dataSource; + try { + dataSource = context.getInjectedReference(DataSources.class).getDataSource(dataSourceName); + if (dataSource instanceof UnconfiguredDataSource) { + throw DataSourceUtil.dataSourceNotConfigured(dataSourceName); + } + } catch (RuntimeException e) { + throw new UnsatisfiedResolutionException(String.format(Locale.ROOT, + "Unable to find datasource '%s' for Liquibase: %s", + dataSourceName, e.getMessage()), e); } LiquibaseFactoryProducer liquibaseProducer = context.getInjectedReference(LiquibaseFactoryProducer.class);