From cba20c1312c2e02793cdf1b207eddaec6c37c77f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 27 Jul 2022 10:02:37 +0200 Subject: [PATCH 01/13] Move *all* logfilter-related build steps to HibernateLogFilterBuildStep --- .../orm/deployment/HibernateLogFilterBuildStep.java | 8 ++++++++ .../hibernate/orm/deployment/HibernateOrmProcessor.java | 7 ------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateLogFilterBuildStep.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateLogFilterBuildStep.java index f30a4f579b6fa..49f9bf48e7a3c 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateLogFilterBuildStep.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateLogFilterBuildStep.java @@ -2,13 +2,21 @@ import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.NativeImageFeatureBuildItem; import io.quarkus.deployment.logging.LogCleanupFilterBuildItem; +import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild; +import io.quarkus.hibernate.orm.runtime.graal.DisableLoggingFeature; /** * Processor that sets up log filters for Hibernate */ public final class HibernateLogFilterBuildStep { + @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class) + NativeImageFeatureBuildItem nativeImageFeature() { + return new NativeImageFeatureBuildItem(DisableLoggingFeature.class); + } + @BuildStep void setupLogFilters(BuildProducer filters) { filters.produce(new LogCleanupFilterBuildItem("org.hibernate.Version", "HHH000412")); 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 50b8f12541032..2f9d5ba1ef30b 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 @@ -103,7 +103,6 @@ import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.deployment.builditem.LiveReloadBuildItem; import io.quarkus.deployment.builditem.LogCategoryBuildItem; -import io.quarkus.deployment.builditem.NativeImageFeatureBuildItem; import io.quarkus.deployment.builditem.ServiceStartBuildItem; import io.quarkus.deployment.builditem.SystemPropertyBuildItem; import io.quarkus.deployment.builditem.TransformedClassesBuildItem; @@ -137,7 +136,6 @@ import io.quarkus.hibernate.orm.runtime.cdi.QuarkusArcBeanContainer; import io.quarkus.hibernate.orm.runtime.devconsole.HibernateOrmDevConsoleCreateDDLSupplier; import io.quarkus.hibernate.orm.runtime.devconsole.HibernateOrmDevConsoleIntegrator; -import io.quarkus.hibernate.orm.runtime.graal.DisableLoggingFeature; import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticDescriptor; import io.quarkus.hibernate.orm.runtime.migration.MultiTenancyStrategy; import io.quarkus.hibernate.orm.runtime.proxies.PreGeneratedProxies; @@ -172,11 +170,6 @@ public final class HibernateOrmProcessor { private static final String INTEGRATOR_SERVICE_FILE = "META-INF/services/org.hibernate.integrator.spi.Integrator"; - @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class) - NativeImageFeatureBuildItem nativeImageFeature() { - return new NativeImageFeatureBuildItem(DisableLoggingFeature.class); - } - @BuildStep void registerHibernateOrmMetadataForCoreDialects( BuildProducer producer) { From a536b9528b177d8806259b318224201ca9d19f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 27 Jul 2022 10:04:13 +0200 Subject: [PATCH 02/13] Add build-time configuration property quarkus.hibernate-orm.enabled --- .../HibernateLogFilterBuildStep.java | 2 + .../HibernateOrmAlwaysEnabledProcessor.java | 15 +++++ .../deployment/HibernateOrmCdiProcessor.java | 4 +- .../orm/deployment/HibernateOrmConfig.java | 11 ++++ .../orm/deployment/HibernateOrmEnabled.java | 22 +++++++ .../orm/deployment/HibernateOrmProcessor.java | 7 +-- .../HibernateUserTypeProcessor.java | 2 + ...asyReactiveServerIntegrationProcessor.java | 2 + .../HibernateDevConsoleProcessor.java | 3 + .../metrics/HibernateOrmMetricsProcessor.java | 3 + .../ConfigEnabledFalseAndEntityTest.java | 57 +++++++++++++++++++ ...bernateReactiveAlwaysEnabledProcessor.java | 15 +++++ .../deployment/HibernateReactiveEnabled.java | 30 ++++++++++ .../HibernateReactiveLogFilter.java | 4 +- .../HibernateReactiveProcessor.java | 19 ++++--- ...asyReactiveServerIntegrationProcessor.java | 2 + .../ConfigEnabledFalseAndEntityTest.java | 39 +++++++++++++ .../hibernate/reactive/config/MyEntity.java | 31 ++++++++++ ...nacheHibernateCommonResourceProcessor.java | 19 +++++-- 19 files changed, 267 insertions(+), 20 deletions(-) create mode 100644 extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmAlwaysEnabledProcessor.java create mode 100644 extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmEnabled.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigEnabledFalseAndEntityTest.java create mode 100644 extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveAlwaysEnabledProcessor.java create mode 100644 extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveEnabled.java create mode 100644 extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigEnabledFalseAndEntityTest.java create mode 100644 extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/MyEntity.java diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateLogFilterBuildStep.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateLogFilterBuildStep.java index 49f9bf48e7a3c..35710e943e88f 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateLogFilterBuildStep.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateLogFilterBuildStep.java @@ -2,6 +2,7 @@ import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.deployment.builditem.NativeImageFeatureBuildItem; import io.quarkus.deployment.logging.LogCleanupFilterBuildItem; import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild; @@ -10,6 +11,7 @@ /** * Processor that sets up log filters for Hibernate */ +@BuildSteps(onlyIf = HibernateOrmEnabled.class) public final class HibernateLogFilterBuildStep { @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class) diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmAlwaysEnabledProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmAlwaysEnabledProcessor.java new file mode 100644 index 0000000000000..e1282ea743b97 --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmAlwaysEnabledProcessor.java @@ -0,0 +1,15 @@ +package io.quarkus.hibernate.orm.deployment; + +import io.quarkus.deployment.Feature; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; + +// Executed even if the extension is disabled, see https://github.com/quarkusio/quarkus/pull/26966/ +public class HibernateOrmAlwaysEnabledProcessor { + + @BuildStep + public FeatureBuildItem featureBuildItem() { + return new FeatureBuildItem(Feature.HIBERNATE_ORM); + } + +} diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java index d5cad67977935..94aaf77a05e94 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java @@ -29,12 +29,14 @@ import io.quarkus.arc.processor.Transformation; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; import io.quarkus.hibernate.orm.PersistenceUnit; import io.quarkus.hibernate.orm.runtime.HibernateOrmRecorder; import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil; +@BuildSteps(onlyIf = HibernateOrmEnabled.class) public class HibernateOrmCdiProcessor { private static final List SESSION_FACTORY_EXPOSED_TYPES = Arrays.asList(ClassNames.ENTITY_MANAGER_FACTORY, @@ -168,8 +170,6 @@ void registerAnnotations(BuildProducer additionalBeans, .build()); // Register the default scope for @PersistenceUnitExtension and make such beans unremovable by default - // TODO make @PUExtension beans unremovable only if the corresponding PU actually exists and is enabled - // (I think there's a feature request for a configuration property to disable a PU at runtime?) beanDefiningAnnotations .produce(new BeanDefiningAnnotationBuildItem(ClassNames.PERSISTENCE_UNIT_EXTENSION, DotNames.APPLICATION_SCOPED, false)); diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java index cff9da039e318..f7280638b1d74 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java @@ -14,6 +14,17 @@ @ConfigRoot public class HibernateOrmConfig { + /** + * Whether Hibernate ORM is enabled during the build. + * + * If Hibernate ORM is disabled during the build, all processing related to Hibernate ORM will be skipped, + * but it will not be possible to use Hibernate ORM at runtime. + * + * @asciidoclet + */ + @ConfigItem(defaultValue = "true") + public boolean enabled; + /** * Configuration for the default persistence unit. */ diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmEnabled.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmEnabled.java new file mode 100644 index 0000000000000..b5ed9ddb3183c --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmEnabled.java @@ -0,0 +1,22 @@ +package io.quarkus.hibernate.orm.deployment; + +import java.util.function.BooleanSupplier; + +/** + * Supplier that can be used to only run build steps + * if the Hibernate ORM extension is enabled. + */ +public class HibernateOrmEnabled implements BooleanSupplier { + + private final HibernateOrmConfig config; + + HibernateOrmEnabled(HibernateOrmConfig config) { + this.config = config; + } + + @Override + public boolean getAsBoolean() { + return config.enabled; + } + +} 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 2f9d5ba1ef30b..7ca7acdd5683b 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 @@ -82,11 +82,11 @@ import io.quarkus.datasource.common.runtime.DatabaseKind; import io.quarkus.deployment.Capabilities; import io.quarkus.deployment.Capability; -import io.quarkus.deployment.Feature; import io.quarkus.deployment.IsDevelopment; import io.quarkus.deployment.IsNormal; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.deployment.annotations.Consume; import io.quarkus.deployment.annotations.Produce; import io.quarkus.deployment.annotations.Record; @@ -97,7 +97,6 @@ import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.DevServicesAdditionalConfigBuildItem; -import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem; import io.quarkus.deployment.builditem.LaunchModeBuildItem; @@ -161,6 +160,7 @@ * @author Emmanuel Bernard emmanuel@hibernate.org * @author Sanne Grinovero */ +@BuildSteps(onlyIf = HibernateOrmEnabled.class) public final class HibernateOrmProcessor { public static final String HIBERNATE_ORM_CONFIG_PREFIX = "quarkus.hibernate-orm."; @@ -556,11 +556,8 @@ public void build(RecorderContext recorderContext, HibernateOrmRecorder recorder JpaModelBuildItem jpaModel, List persistenceUnitDescriptorBuildItems, List integrationBuildItems, - BuildProducer feature, BuildProducer beanContainerListener, LaunchModeBuildItem launchMode) throws Exception { - - feature.produce(new FeatureBuildItem(Feature.HIBERNATE_ORM)); validateHibernatePropertiesNotUsed(); final boolean enableORM = hasEntities(jpaModel); diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateUserTypeProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateUserTypeProcessor.java index 31923da3643a7..edf031dcd0dde 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateUserTypeProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateUserTypeProcessor.java @@ -12,9 +12,11 @@ import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +@BuildSteps(onlyIf = HibernateOrmEnabled.class) public final class HibernateUserTypeProcessor { private static final String TYPE_VALUE = "type"; private static final String TYPE_CLASS_VALUE = "typeClass"; diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ResteasyReactiveServerIntegrationProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ResteasyReactiveServerIntegrationProcessor.java index b91aed743bfef..3c1800fd2969f 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ResteasyReactiveServerIntegrationProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ResteasyReactiveServerIntegrationProcessor.java @@ -3,8 +3,10 @@ import javax.persistence.PersistenceException; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.resteasy.reactive.server.spi.UnwrappedExceptionBuildItem; +@BuildSteps(onlyIf = HibernateOrmEnabled.class) public class ResteasyReactiveServerIntegrationProcessor { @BuildStep diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/devconsole/HibernateDevConsoleProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/devconsole/HibernateDevConsoleProcessor.java index 2eb107013ad52..277e83fb7e5b4 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/devconsole/HibernateDevConsoleProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/devconsole/HibernateDevConsoleProcessor.java @@ -2,10 +2,13 @@ import io.quarkus.deployment.IsDevelopment; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; import io.quarkus.devconsole.spi.DevConsoleRuntimeTemplateInfoBuildItem; +import io.quarkus.hibernate.orm.deployment.HibernateOrmEnabled; import io.quarkus.hibernate.orm.runtime.devconsole.HibernateOrmDevConsoleInfoSupplier; +@BuildSteps(onlyIf = HibernateOrmEnabled.class) public class HibernateDevConsoleProcessor { @BuildStep(onlyIf = IsDevelopment.class) diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/metrics/HibernateOrmMetricsProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/metrics/HibernateOrmMetricsProcessor.java index 14eb9a3f0583a..de04d1dc662a4 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/metrics/HibernateOrmMetricsProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/metrics/HibernateOrmMetricsProcessor.java @@ -7,10 +7,12 @@ import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem; import io.quarkus.deployment.metrics.MetricsFactoryConsumerBuildItem; import io.quarkus.hibernate.orm.deployment.HibernateOrmConfig; +import io.quarkus.hibernate.orm.deployment.HibernateOrmEnabled; import io.quarkus.hibernate.orm.deployment.PersistenceProviderSetUpBuildItem; import io.quarkus.hibernate.orm.runtime.metrics.HibernateMetricsRecorder; @@ -18,6 +20,7 @@ * Produce metrics for Hibernate ORM * Avoid hard dependencies in main processor */ +@BuildSteps(onlyIf = HibernateOrmEnabled.class) public final class HibernateOrmMetricsProcessor { @BuildStep diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigEnabledFalseAndEntityTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigEnabledFalseAndEntityTest.java new file mode 100644 index 0000000000000..a4f41f7d473ce --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigEnabledFalseAndEntityTest.java @@ -0,0 +1,57 @@ +package io.quarkus.hibernate.orm.config; + +import static org.assertj.core.api.Assertions.assertThat; + +import javax.enterprise.context.control.ActivateRequestContext; +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.test.QuarkusUnitTest; + +public class ConfigEnabledFalseAndEntityTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest().setArchiveProducer( + () -> ShrinkWrap.create(JavaArchive.class).addClass(MyEntity.class)) + .withConfigurationResource("application.properties") + // This should disable Hibernate ORM even if there is an entity + .overrideConfigKey("quarkus.hibernate-orm.enabled", "false"); + + @Test + public void entityManagerFactory() { + // The bean is not defined during static init, so it's null. + assertThat(Arc.container().instance(EntityManagerFactory.class).get()) + .isNull(); + } + + @Test + public void sessionFactory() { + // The bean is not defined during static init, so it's null. + assertThat(Arc.container().instance(SessionFactory.class).get()) + .isNull(); + } + + @Test + @ActivateRequestContext + public void entityManager() { + // The bean is not defined during static init, so it's null. + assertThat(Arc.container().instance(EntityManager.class).get()) + .isNull(); + } + + @Test + @ActivateRequestContext + public void session() { + // The bean is not defined during static init, so it's null. + assertThat(Arc.container().instance(Session.class).get()) + .isNull(); + } +} diff --git a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveAlwaysEnabledProcessor.java b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveAlwaysEnabledProcessor.java new file mode 100644 index 0000000000000..ba05697abccf7 --- /dev/null +++ b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveAlwaysEnabledProcessor.java @@ -0,0 +1,15 @@ +package io.quarkus.hibernate.reactive.deployment; + +import io.quarkus.deployment.Feature; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; + +// Executed even if the extension is disabled, see https://github.com/quarkusio/quarkus/pull/26966/ +public final class HibernateReactiveAlwaysEnabledProcessor { + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(Feature.HIBERNATE_REACTIVE); + } + +} diff --git a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveEnabled.java b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveEnabled.java new file mode 100644 index 0000000000000..b99f73e8ee4e8 --- /dev/null +++ b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveEnabled.java @@ -0,0 +1,30 @@ +package io.quarkus.hibernate.reactive.deployment; + +import java.util.function.BooleanSupplier; + +import io.quarkus.hibernate.orm.deployment.HibernateOrmConfig; + +/** + * Supplier that can be used to only run build steps + * if the Hibernate ORM extension is enabled. + */ +// TODO Ideally we'd rely on separate configuration for Hibernate Reactive, +// both in general and specifically to enable/disable the extension. +// But we would first need to split common code to a separate artifact, +// and that's a lot of work that will conflict with other ongoing changes, +// so we better wait. +// See also https://github.com/quarkusio/quarkus/issues/13425 +public class HibernateReactiveEnabled implements BooleanSupplier { + + private final HibernateOrmConfig config; + + HibernateReactiveEnabled(HibernateOrmConfig config) { + this.config = config; + } + + @Override + public boolean getAsBoolean() { + return config.enabled; + } + +} diff --git a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveLogFilter.java b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveLogFilter.java index 4ec398b290638..f529fd37ceefa 100644 --- a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveLogFilter.java +++ b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveLogFilter.java @@ -2,8 +2,10 @@ import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.deployment.logging.LogCleanupFilterBuildItem; +@BuildSteps(onlyIf = HibernateReactiveEnabled.class) public final class HibernateReactiveLogFilter { @BuildStep @@ -12,4 +14,4 @@ void setupLogFilters(BuildProducer filters) { "org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator", "HHH000181")); } -} \ No newline at end of file +} diff --git a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java index be725d6285adb..c2d7a763eab7f 100644 --- a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java +++ b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java @@ -32,13 +32,12 @@ import io.quarkus.arc.deployment.RecorderBeanInitializedBuildItem; import io.quarkus.datasource.deployment.spi.DefaultDataSourceDbKindBuildItem; import io.quarkus.datasource.runtime.DataSourcesBuildTimeConfig; -import io.quarkus.deployment.Feature; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; -import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem; import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.deployment.builditem.SystemPropertyBuildItem; @@ -46,7 +45,15 @@ import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; import io.quarkus.deployment.recording.RecorderContext; -import io.quarkus.hibernate.orm.deployment.*; +import io.quarkus.hibernate.orm.deployment.Dialects; +import io.quarkus.hibernate.orm.deployment.HibernateConfigUtil; +import io.quarkus.hibernate.orm.deployment.HibernateOrmConfig; +import io.quarkus.hibernate.orm.deployment.HibernateOrmConfigPersistenceUnit; +import io.quarkus.hibernate.orm.deployment.HibernateOrmProcessor; +import io.quarkus.hibernate.orm.deployment.JpaModelBuildItem; +import io.quarkus.hibernate.orm.deployment.PersistenceProviderSetUpBuildItem; +import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem; +import io.quarkus.hibernate.orm.deployment.PersistenceXmlDescriptorBuildItem; import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem; import io.quarkus.hibernate.orm.deployment.spi.DatabaseKindDialectBuildItem; import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig; @@ -59,6 +66,7 @@ import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.configuration.ConfigurationException; +@BuildSteps(onlyIf = HibernateReactiveEnabled.class) public final class HibernateReactiveProcessor { private static final String HIBERNATE_REACTIVE = "Hibernate Reactive"; @@ -71,11 +79,6 @@ public final class HibernateReactiveProcessor { "org.hibernate.reactive.persister.collection.impl.ReactiveBasicCollectionPersister", }; - @BuildStep - FeatureBuildItem feature() { - return new FeatureBuildItem(Feature.HIBERNATE_REACTIVE); - } - @BuildStep void registerBeans(BuildProducer additionalBeans, CombinedIndexBuildItem combinedIndex, List descriptors, diff --git a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/ResteasyReactiveServerIntegrationProcessor.java b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/ResteasyReactiveServerIntegrationProcessor.java index 3ad61c5a1aed7..18e9233a30be4 100644 --- a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/ResteasyReactiveServerIntegrationProcessor.java +++ b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/ResteasyReactiveServerIntegrationProcessor.java @@ -3,8 +3,10 @@ import javax.persistence.PersistenceException; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.resteasy.reactive.server.spi.UnwrappedExceptionBuildItem; +@BuildSteps(onlyIf = HibernateReactiveEnabled.class) public class ResteasyReactiveServerIntegrationProcessor { @BuildStep diff --git a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigEnabledFalseAndEntityTest.java b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigEnabledFalseAndEntityTest.java new file mode 100644 index 0000000000000..5e28603b0dec2 --- /dev/null +++ b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigEnabledFalseAndEntityTest.java @@ -0,0 +1,39 @@ +package io.quarkus.hibernate.reactive.config; + +import static org.assertj.core.api.Assertions.assertThat; + +import javax.enterprise.context.control.ActivateRequestContext; + +import org.hibernate.reactive.mutiny.Mutiny; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.test.QuarkusUnitTest; + +public class ConfigEnabledFalseAndEntityTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest().setArchiveProducer( + () -> ShrinkWrap.create(JavaArchive.class).addClass(MyEntity.class)) + .withConfigurationResource("application.properties") + // This should disable Hibernate Reactive even if there is an entity + .overrideConfigKey("quarkus.hibernate-orm.enabled", "false"); + + @Test + public void sessionFactory() { + // The bean is not defined during static init, so it's null. + assertThat(Arc.container().instance(Mutiny.SessionFactory.class).get()) + .isNull(); + } + + @Test + @ActivateRequestContext + public void session() { + // The bean is not defined during static init, so it's null. + assertThat(Arc.container().instance(Mutiny.Session.class).get()) + .isNull(); + } +} diff --git a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/MyEntity.java b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/MyEntity.java new file mode 100644 index 0000000000000..e423c9f82a47b --- /dev/null +++ b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/MyEntity.java @@ -0,0 +1,31 @@ +package io.quarkus.hibernate.reactive.config; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class MyEntity { + @Id + private long id; + + private String name; + + public MyEntity() { + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/extensions/panache/panache-hibernate-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheHibernateCommonResourceProcessor.java b/extensions/panache/panache-hibernate-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheHibernateCommonResourceProcessor.java index eb2536d0675d5..ab4bf1ef76209 100644 --- a/extensions/panache/panache-hibernate-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheHibernateCommonResourceProcessor.java +++ b/extensions/panache/panache-hibernate-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheHibernateCommonResourceProcessor.java @@ -5,6 +5,7 @@ import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import javax.persistence.Transient; @@ -37,13 +38,18 @@ public final class PanacheHibernateCommonResourceProcessor { // needed for HibernateEnhancersRegisteredBuildItem @BuildStep void findEntityClasses(CombinedIndexBuildItem index, - HibernateModelClassCandidatesForFieldAccessBuildItem candidatesForFieldAccess, + Optional candidatesForFieldAccess, BuildProducer modelInfoBuildItem, BuildProducer fieldAccessEnhancedEntityClasses) { + if (candidatesForFieldAccess.isEmpty()) { + // Hibernate ORM is disabled + return; + } + MetamodelInfo modelInfo = new MetamodelInfo(); // Technically we wouldn't need to process embeddables, but we don't have an easy way to exclude them. - for (String entityClassName : candidatesForFieldAccess.getAllModelClassNames()) { + for (String entityClassName : candidatesForFieldAccess.get().getAllModelClassNames()) { ClassInfo entityClass = index.getIndex().getClassByName(DotName.createSimple(entityClassName)); if (entityClass == null) { // Probably a synthetic entity, such as Envers' DefaultRevisionEntity. @@ -77,9 +83,14 @@ void findEntityClasses(CombinedIndexBuildItem index, @Consume(HibernateEnhancersRegisteredBuildItem.class) void replaceFieldAccesses(CombinedIndexBuildItem index, ApplicationArchivesBuildItem applicationArchivesBuildItem, - HibernateMetamodelForFieldAccessBuildItem modelInfoBuildItem, + Optional modelInfoBuildItem, BuildProducer transformers) { - MetamodelInfo modelInfo = modelInfoBuildItem.getMetamodelInfo(); + if (modelInfoBuildItem.isEmpty()) { + // Hibernate ORM is disabled + return; + } + + MetamodelInfo modelInfo = modelInfoBuildItem.get().getMetamodelInfo(); Set entitiesWithPublicFields = modelInfo.getEntitiesWithPublicFields(); if (entitiesWithPublicFields.isEmpty()) { // There are no public fields to be accessed in the first place. From 4328701752a4fdfdbf5655dfc5c6b10e0d407341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 2 Aug 2022 08:46:10 +0200 Subject: [PATCH 03/13] Make Hibernate Reactive SessionFactory/Session beans unremovable So that an application using only programmatic bean retrieval will work correctly. This is useful for our own tests, but I think it makes sense in general too, and that's how Hibernate ORM's SessionFactory/Session beans are defined too. --- .../reactive/runtime/ReactiveSessionFactoryProducer.java | 2 ++ .../hibernate/reactive/runtime/ReactiveSessionProducer.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionFactoryProducer.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionFactoryProducer.java index 478f5a60c23bc..0fb284d85ac04 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionFactoryProducer.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionFactoryProducer.java @@ -10,6 +10,7 @@ import org.hibernate.reactive.mutiny.Mutiny; import io.quarkus.arc.DefaultBean; +import io.quarkus.arc.Unremovable; public class ReactiveSessionFactoryProducer { @@ -20,6 +21,7 @@ public class ReactiveSessionFactoryProducer { @Produces @Singleton @DefaultBean + @Unremovable @Typed(Mutiny.SessionFactory.class) public Mutiny.SessionFactory mutinySessionFactory() { return emf.unwrap(Mutiny.SessionFactory.class); diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionProducer.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionProducer.java index 5d0a9f8c3b58e..11fe0b17e6c09 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionProducer.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionProducer.java @@ -11,6 +11,7 @@ import org.hibernate.reactive.mutiny.Mutiny; import io.quarkus.arc.DefaultBean; +import io.quarkus.arc.Unremovable; public class ReactiveSessionProducer { @@ -20,6 +21,7 @@ public class ReactiveSessionProducer { @Produces @RequestScoped @DefaultBean + @Unremovable public Mutiny.Session createMutinySession() { return ((MutinyImplementor) mutinySessionFactory).newSession(); } From 80eb73c2532fd7d426d25db851a214a25f77b7f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 2 Aug 2022 09:37:55 +0200 Subject: [PATCH 04/13] Change the scope of Mutiny.SessionFactory from Singleton to ApplicationScoped For the same reasons as Hibernate ORM: 1. So that it can be mocked 2. More importantly, so that it is instantiated on first use (i.e. at runtime) and so that we can throw an exception when the persistence unit was deactivated through configuration properties. --- .../runtime/ReactiveSessionFactoryProducer.java | 13 ++++++++----- .../reactive/runtime/ReactiveSessionProducer.java | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionFactoryProducer.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionFactoryProducer.java index 0fb284d85ac04..51d0564e0aa1b 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionFactoryProducer.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionFactoryProducer.java @@ -1,13 +1,15 @@ package io.quarkus.hibernate.reactive.runtime; +import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Produces; import javax.enterprise.inject.Typed; import javax.inject.Inject; -import javax.inject.Singleton; import javax.persistence.EntityManagerFactory; import javax.persistence.PersistenceUnit; +import org.hibernate.reactive.common.spi.MutinyImplementor; import org.hibernate.reactive.mutiny.Mutiny; +import org.hibernate.reactive.mutiny.impl.MutinySessionFactoryImpl; import io.quarkus.arc.DefaultBean; import io.quarkus.arc.Unremovable; @@ -19,12 +21,13 @@ public class ReactiveSessionFactoryProducer { EntityManagerFactory emf; @Produces - @Singleton + @ApplicationScoped @DefaultBean @Unremovable - @Typed(Mutiny.SessionFactory.class) - public Mutiny.SessionFactory mutinySessionFactory() { - return emf.unwrap(Mutiny.SessionFactory.class); + @Typed({ Mutiny.SessionFactory.class, MutinyImplementor.class }) + public MutinySessionFactoryImpl mutinySessionFactory() { + // TODO Remove this cast when we get rid of the dependency to MutinyImplementor + return (MutinySessionFactoryImpl) emf.unwrap(Mutiny.SessionFactory.class); } } diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionProducer.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionProducer.java index 11fe0b17e6c09..2dd1639a0d44d 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionProducer.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionProducer.java @@ -16,14 +16,14 @@ public class ReactiveSessionProducer { @Inject - Mutiny.SessionFactory mutinySessionFactory; + MutinyImplementor mutinyImplementor; @Produces @RequestScoped @DefaultBean @Unremovable public Mutiny.Session createMutinySession() { - return ((MutinyImplementor) mutinySessionFactory).newSession(); + return mutinyImplementor.newSession(); } public void disposeMutinySession(@Disposes Mutiny.Session reactiveSession) { From 477c086725456c9f5b4c5d2161db5b22239a6a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 27 Jul 2022 13:48:41 +0200 Subject: [PATCH 05/13] Simplify fetching of runtime PU config in Hibernate ORM/Reactive --- .../FastBootHibernatePersistenceProvider.java | 17 +++++++---------- .../orm/runtime/HibernateOrmRuntimeConfig.java | 10 ++++++++++ ...ootHibernateReactivePersistenceProvider.java | 12 +++++++----- 3 files changed, 24 insertions(+), 15 deletions(-) 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 c1b82910f8b7c..bb848c38c8bc1 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 @@ -146,6 +146,8 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String throw new PersistenceException("No name provided and multiple persistence units found"); } + Map puConfigMap = hibernateOrmRuntimeConfig + .getAllPersistenceUnitConfigsAsMap(); for (PersistenceUnitDescriptor persistenceUnit : units) { log.debugf( "Checking persistence-unit [name=%s, explicit-provider=%s] against incoming persistence unit name [%s]", @@ -171,7 +173,9 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String "Attempting to boot a blocking Hibernate ORM instance on a reactive RecordedState"); } final PrevalidatedQuarkusMetadata metadata = recordedState.getMetadata(); - RuntimeSettings runtimeSettings = buildRuntimeSettings(persistenceUnitName, recordedState); + var puConfig = puConfigMap.getOrDefault(persistenceUnitName, + new HibernateOrmRuntimeConfigPersistenceUnit()); + RuntimeSettings runtimeSettings = buildRuntimeSettings(persistenceUnitName, recordedState, puConfig); StandardServiceRegistry standardServiceRegistry = rewireMetadataAndExtractServiceRegistry(runtimeSettings, recordedState, persistenceUnitName); @@ -191,7 +195,8 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String return null; } - private RuntimeSettings buildRuntimeSettings(String persistenceUnitName, RecordedState recordedState) { + private RuntimeSettings buildRuntimeSettings(String persistenceUnitName, RecordedState recordedState, + HibernateOrmRuntimeConfigPersistenceUnit persistenceUnitConfig) { final BuildTimeSettings buildTimeSettings = recordedState.getBuildTimeSettings(); final IntegrationSettings integrationSettings = recordedState.getIntegrationSettings(); Builder runtimeSettingsBuilder = new Builder(buildTimeSettings, integrationSettings); @@ -202,14 +207,6 @@ private RuntimeSettings buildRuntimeSettings(String persistenceUnitName, Recorde injectDataSource(persistenceUnitName, dataSourceName.get(), runtimeSettingsBuilder); } - HibernateOrmRuntimeConfigPersistenceUnit persistenceUnitConfig; - if (PersistenceUnitUtil.isDefaultPersistenceUnit(persistenceUnitName)) { - persistenceUnitConfig = hibernateOrmRuntimeConfig.defaultPersistenceUnit; - } else { - persistenceUnitConfig = hibernateOrmRuntimeConfig.persistenceUnits.getOrDefault(persistenceUnitName, - new HibernateOrmRuntimeConfigPersistenceUnit()); - } - // Inject runtime configuration if the persistence unit was defined by Quarkus configuration if (!recordedState.isFromPersistenceXml()) { injectRuntimeConfiguration(persistenceUnitConfig, runtimeSettingsBuilder); diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfig.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfig.java index 00d3b1e0df243..e7c4483ffc011 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfig.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfig.java @@ -1,6 +1,7 @@ package io.quarkus.hibernate.orm.runtime; import java.util.Map; +import java.util.TreeMap; import io.quarkus.runtime.annotations.ConfigDocMapKey; import io.quarkus.runtime.annotations.ConfigDocSection; @@ -25,6 +26,15 @@ public class HibernateOrmRuntimeConfig { @ConfigItem(name = ConfigItem.PARENT) public Map persistenceUnits; + public Map getAllPersistenceUnitConfigsAsMap() { + Map map = new TreeMap<>(); + if (defaultPersistenceUnit != null) { + map.put(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, defaultPersistenceUnit); + } + map.putAll(persistenceUnits); + return map; + } + public static String puPropertyKey(String puName, String radical) { String prefix = PersistenceUnitUtil.isDefaultPersistenceUnit(puName) ? "quarkus.hibernate-orm." diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java index 2357a59c73c7f..7595c8c5eeffa 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java @@ -116,6 +116,8 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String throw new PersistenceException("No name provided and multiple persistence units found"); } + Map puConfigMap = hibernateOrmRuntimeConfig + .getAllPersistenceUnitConfigsAsMap(); for (PersistenceUnitDescriptor persistenceUnit : units) { log.debugf( "Checking persistence-unit [name=%s, explicit-provider=%s] against incoming persistence unit name [%s]", @@ -144,7 +146,9 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String // Inject runtime configuration if the persistence unit was defined by Quarkus configuration if (!recordedState.isFromPersistenceXml()) { - injectRuntimeConfiguration(persistenceUnitName, hibernateOrmRuntimeConfig, runtimeSettingsBuilder); + var puConfig = puConfigMap.getOrDefault(persistenceUnitName, + new HibernateOrmRuntimeConfigPersistenceUnit()); + injectRuntimeConfiguration(puConfig, runtimeSettingsBuilder); } for (HibernateOrmIntegrationRuntimeDescriptor descriptor : integrationRuntimeDescriptors @@ -261,10 +265,8 @@ private void registerVertxAndPool(String persistenceUnitName, serviceRegistry.addInitiator(new VertxInstanceInitiator(vertxHandle.get())); } - private static void injectRuntimeConfiguration(String persistenceUnitName, - HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, Builder runtimeSettingsBuilder) { - HibernateOrmRuntimeConfigPersistenceUnit persistenceUnitConfig = hibernateOrmRuntimeConfig.defaultPersistenceUnit; - + private static void injectRuntimeConfiguration(HibernateOrmRuntimeConfigPersistenceUnit persistenceUnitConfig, + Builder runtimeSettingsBuilder) { // Database runtimeSettingsBuilder.put(AvailableSettings.HBM2DDL_DATABASE_ACTION, persistenceUnitConfig.database.generation.generation); From e9b4dcef561a207fb922437443b1557b2e113ade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 2 Aug 2022 12:38:12 +0200 Subject: [PATCH 06/13] Remove the unnecessary bean JPAConfigSupport --- .../orm/deployment/HibernateOrmProcessor.java | 14 -------------- .../orm/runtime/HibernateOrmRecorder.java | 9 --------- .../quarkus/hibernate/orm/runtime/JPAConfig.java | 9 +++++---- .../hibernate/orm/runtime/JPAConfigSupport.java | 15 --------------- 4 files changed, 5 insertions(+), 42 deletions(-) delete mode 100644 extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/JPAConfigSupport.java 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 7ca7acdd5683b..4c9c5448b6cef 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 @@ -34,7 +34,6 @@ import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Default; -import javax.inject.Singleton; import javax.persistence.AttributeConverter; import javax.persistence.SharedCacheMode; import javax.persistence.ValidationMode; @@ -123,7 +122,6 @@ import io.quarkus.hibernate.orm.runtime.HibernateOrmRecorder; import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig; import io.quarkus.hibernate.orm.runtime.JPAConfig; -import io.quarkus.hibernate.orm.runtime.JPAConfigSupport; import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil; import io.quarkus.hibernate.orm.runtime.RequestScopedSessionHolder; import io.quarkus.hibernate.orm.runtime.TransactionSessions; @@ -706,19 +704,14 @@ public HibernateModelClassCandidatesForFieldAccessBuildItem candidatesForFieldAc @Record(STATIC_INIT) public void build(HibernateOrmRecorder recorder, HibernateOrmConfig hibernateOrmConfig, BuildProducer jpaModelPersistenceUnitMapping, - BuildProducer syntheticBeans, List descriptors, JpaModelBuildItem jpaModel) throws Exception { if (!hasEntities(jpaModel)) { return; } - Set persistenceUnitNames = new HashSet<>(); - Map> entityPersistenceUnitMapping = new HashMap<>(); for (PersistenceUnitDescriptorBuildItem descriptor : descriptors) { - persistenceUnitNames.add(descriptor.getPersistenceUnitName()); - for (String entityClass : descriptor.getManagedClassNames()) { entityPersistenceUnitMapping.putIfAbsent(entityClass, new HashSet<>()); entityPersistenceUnitMapping.get(entityClass).add(descriptor.getPersistenceUnitName()); @@ -726,13 +719,6 @@ public void build(HibernateOrmRecorder recorder, HibernateOrmConfig hibernateOrm } jpaModelPersistenceUnitMapping.produce(new JpaModelPersistenceUnitMappingBuildItem(entityPersistenceUnitMapping)); - - syntheticBeans.produce(SyntheticBeanBuildItem.configure(JPAConfigSupport.class) - .scope(Singleton.class) - .unremovable() - .supplier(recorder.jpaConfigSupportSupplier( - new JPAConfigSupport(persistenceUnitNames))) - .done()); } @BuildStep diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java index 98b039a916e20..f5c22b8521543 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java @@ -79,15 +79,6 @@ public void created(BeanContainer beanContainer) { }; } - public Supplier jpaConfigSupportSupplier(JPAConfigSupport jpaConfigSupport) { - return new Supplier() { - @Override - public JPAConfigSupport get() { - return jpaConfigSupport; - } - }; - } - public Supplier dataSourceTenantConnectionResolver(String persistenceUnitName, Optional dataSourceName, MultiTenancyStrategy multiTenancyStrategy, String multiTenancySchemaDataSourceName) { 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 d1186090e1f1b..53a5a098ae480 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 @@ -18,6 +18,7 @@ import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; +import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; import org.jboss.logging.Logger; @Singleton @@ -28,11 +29,11 @@ public class JPAConfig { private final Map persistenceUnits; @Inject - public JPAConfig(JPAConfigSupport jpaConfigSupport) { - + public JPAConfig() { Map persistenceUnitsBuilder = new HashMap<>(); - for (String persistenceUnitName : jpaConfigSupport.persistenceUnitNames) { - persistenceUnitsBuilder.put(persistenceUnitName, new LazyPersistenceUnit(persistenceUnitName)); + for (PersistenceUnitDescriptor descriptor : PersistenceUnitsHolder.getPersistenceUnitDescriptors()) { + String puName = descriptor.getName(); + persistenceUnitsBuilder.put(puName, new LazyPersistenceUnit(puName)); } this.persistenceUnits = persistenceUnitsBuilder; } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/JPAConfigSupport.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/JPAConfigSupport.java deleted file mode 100644 index 453ddc7c58052..0000000000000 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/JPAConfigSupport.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.quarkus.hibernate.orm.runtime; - -import java.util.Set; - -public class JPAConfigSupport { - - public Set persistenceUnitNames; - - public JPAConfigSupport() { - } - - public JPAConfigSupport(Set persistenceUnitNames) { - this.persistenceUnitNames = persistenceUnitNames; - } -} From 2e9f9b09831597199b55367216558b40f6472d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 2 Aug 2022 14:00:23 +0200 Subject: [PATCH 07/13] Clarify that Hibernate Reactive uses a different name for its default PU --- .../reactive/deployment/HibernateReactiveProcessor.java | 3 ++- .../quarkus/hibernate/reactive/runtime/HibernateReactive.java | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java index c2d7a763eab7f..53a0e7e1c400b 100644 --- a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java +++ b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java @@ -59,6 +59,7 @@ import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig; import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil; import io.quarkus.hibernate.reactive.runtime.FastBootHibernateReactivePersistenceProvider; +import io.quarkus.hibernate.reactive.runtime.HibernateReactive; import io.quarkus.hibernate.reactive.runtime.HibernateReactiveRecorder; import io.quarkus.hibernate.reactive.runtime.ReactiveSessionFactoryProducer; import io.quarkus.hibernate.reactive.runtime.ReactiveSessionProducer; @@ -224,7 +225,7 @@ private static ParsedPersistenceXmlDescriptor generateReactivePersistenceUnit( // we found one ParsedPersistenceXmlDescriptor desc = new ParsedPersistenceXmlDescriptor(null); //todo URL - desc.setName("default-reactive"); + desc.setName(HibernateReactive.DEFAULT_REACTIVE_PERSISTENCE_UNIT_NAME); desc.setTransactionType(PersistenceUnitTransactionType.RESOURCE_LOCAL); desc.getProperties().setProperty(AvailableSettings.DIALECT, dialect); desc.setExcludeUnlistedClasses(true); diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/HibernateReactive.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/HibernateReactive.java index 62b98e65a7932..9740aa2c3dcc0 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/HibernateReactive.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/HibernateReactive.java @@ -4,6 +4,8 @@ public class HibernateReactive { + public static final String DEFAULT_REACTIVE_PERSISTENCE_UNIT_NAME = "default-reactive"; + public static void featureInit(boolean enabled) { // Override the JPA persistence unit resolver so to use our custom boot // strategy: From a2c90f843b1d1a5c4e73cb77d5d214a4282c4491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 2 Aug 2022 14:56:32 +0200 Subject: [PATCH 08/13] Simplify serialization of QuarkusPersistenceUnitDefinition --- .../orm/deployment/HibernateOrmProcessor.java | 5 - .../boot/LightPersistenceXmlDescriptor.java | 22 +- .../QuarkusPersistenceUnitDefinition.java | 205 +----------------- 3 files changed, 21 insertions(+), 211 deletions(-) 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 4c9c5448b6cef..37f574959a54b 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 @@ -599,11 +599,6 @@ public void build(RecorderContext recorderContext, HibernateOrmRecorder recorder .getOrDefault(pud.getPersistenceUnitName(), Collections.emptyList()))); } - //Make it possible to record the QuarkusPersistenceUnitDefinition as bytecode: - recorderContext.registerSubstitution(QuarkusPersistenceUnitDefinition.class, - QuarkusPersistenceUnitDefinition.Serialized.class, - QuarkusPersistenceUnitDefinition.Substitution.class); - if (hasXmlMappings(persistenceUnitDescriptorBuildItems)) { //Make it possible to record JAXBElement as bytecode: recorderContext.registerSubstitution(JAXBElement.class, diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/LightPersistenceXmlDescriptor.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/LightPersistenceXmlDescriptor.java index a65cdccc93b18..36f03601cee10 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/LightPersistenceXmlDescriptor.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/LightPersistenceXmlDescriptor.java @@ -13,6 +13,8 @@ import org.hibernate.bytecode.enhance.spi.EnhancementContext; import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; +import io.quarkus.runtime.annotations.RecordableConstructor; + public final class LightPersistenceXmlDescriptor implements PersistenceUnitDescriptor { private final String name; @@ -20,24 +22,27 @@ public final class LightPersistenceXmlDescriptor implements PersistenceUnitDescr private final boolean useQuotedIdentifiers; private final PersistenceUnitTransactionType transactionType; private final ValidationMode validationMode; - private final SharedCacheMode sharedCachemode; + private final SharedCacheMode sharedCacheMode; private final List managedClassNames; private final Properties properties; /** - * Internal constructor, as we're trusting all parameters. Useful for serialization to bytecode. - * (intentionally set to package-private visibility) + * @deprecated Do not use directly: this should be considered an internal constructor, + * as we're trusting all parameters. + * Useful for serialization to bytecode (which requires the constructor to be public). */ - LightPersistenceXmlDescriptor(String name, String providerClassName, boolean useQuotedIdentifiers, + @Deprecated + @RecordableConstructor + public LightPersistenceXmlDescriptor(String name, String providerClassName, boolean useQuotedIdentifiers, PersistenceUnitTransactionType transactionType, - ValidationMode validationMode, SharedCacheMode sharedCachemode, List managedClassNames, + ValidationMode validationMode, SharedCacheMode sharedCacheMode, List managedClassNames, Properties properties) { this.name = name; this.providerClassName = providerClassName; this.useQuotedIdentifiers = useQuotedIdentifiers; this.transactionType = transactionType; this.validationMode = validationMode; - this.sharedCachemode = sharedCachemode; + this.sharedCacheMode = sharedCacheMode; this.managedClassNames = managedClassNames; this.properties = properties; } @@ -50,6 +55,7 @@ public final class LightPersistenceXmlDescriptor implements PersistenceUnitDescr * @return a new instance of LightPersistenceXmlDescriptor * @throws UnsupportedOperationException on unsupported configurations */ + @SuppressWarnings("deprecated") public static LightPersistenceXmlDescriptor validateAndReadFrom(PersistenceUnitDescriptor toClone) { Objects.requireNonNull(toClone); verifyIgnoredFields(toClone); @@ -97,7 +103,7 @@ public ValidationMode getValidationMode() { @Override public SharedCacheMode getSharedCacheMode() { - return sharedCachemode; + return sharedCacheMode; } @Override @@ -175,7 +181,7 @@ private static void verifyIgnoredFields(final PersistenceUnitDescriptor toClone) public String toString() { return "PersistenceUnitDescriptor{" + "name='" + name + '\'' + ", providerClassName='" + providerClassName + '\'' + ", useQuotedIdentifiers=" + useQuotedIdentifiers + ", transactionType=" + transactionType - + ", validationMode=" + validationMode + ", sharedCachemode=" + sharedCachemode + ", managedClassNames=" + + ", validationMode=" + validationMode + ", sharedCachemode=" + sharedCacheMode + ", managedClassNames=" + managedClassNames + ", properties=" + properties + '}'; } } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java index 4f33751ba6c71..4c6a3c27907ce 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java @@ -4,18 +4,13 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Properties; - -import javax.persistence.SharedCacheMode; -import javax.persistence.ValidationMode; -import javax.persistence.spi.PersistenceUnitTransactionType; import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; import io.quarkus.hibernate.orm.runtime.boot.xml.RecordableXmlMapping; import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticDescriptor; import io.quarkus.hibernate.orm.runtime.migration.MultiTenancyStrategy; -import io.quarkus.runtime.ObjectSubstitution; +import io.quarkus.runtime.annotations.RecordableConstructor; /** * This represents the fully specified configuration of a Persistence Unit, @@ -49,26 +44,24 @@ public QuarkusPersistenceUnitDefinition(PersistenceUnitDescriptor persistenceUni this.integrationStaticDescriptors = integrationStaticDescriptors; } - /** - * For bytecode deserialization - */ - private QuarkusPersistenceUnitDefinition(LightPersistenceXmlDescriptor persistenceUnitDescriptor, + @RecordableConstructor + public QuarkusPersistenceUnitDefinition(LightPersistenceXmlDescriptor actualHibernateDescriptor, Optional dataSource, MultiTenancyStrategy multitenancyStrategy, List xmlMappings, Map quarkusConfigUnsupportedProperties, - boolean isReactive, + boolean reactive, boolean fromPersistenceXml, List integrationStaticDescriptors) { - Objects.requireNonNull(persistenceUnitDescriptor); + Objects.requireNonNull(actualHibernateDescriptor); Objects.requireNonNull(dataSource); Objects.requireNonNull(multitenancyStrategy); - this.actualHibernateDescriptor = persistenceUnitDescriptor; + this.actualHibernateDescriptor = actualHibernateDescriptor; this.dataSource = dataSource; this.multitenancyStrategy = multitenancyStrategy; this.xmlMappings = xmlMappings; this.quarkusConfigUnsupportedProperties = quarkusConfigUnsupportedProperties; - this.isReactive = isReactive; + this.isReactive = reactive; this.fromPersistenceXml = fromPersistenceXml; this.integrationStaticDescriptors = integrationStaticDescriptors; } @@ -110,188 +103,4 @@ public Map getQuarkusConfigUnsupportedProperties() { return quarkusConfigUnsupportedProperties; } - /** - * This includes the state of both the QuarkusPersistenceUnitDefinition - * and its more complex field of type LightPersistenceXmlDescriptor - */ - public static class Serialized { - - private Optional dataSource; - private MultiTenancyStrategy multitenancyStrategy; - private List xmlMappingBindings; - private boolean isReactive; - private boolean fromPersistenceXml; - private String puName; - private String puProviderClassName; - private boolean puUseQuotedIdentifiers; - private PersistenceUnitTransactionType puTransactionType; - private ValidationMode puValidationMode; - private SharedCacheMode puSharedCachemode; - private List puManagedClassNames; - private Properties puProperties; - private List integrationStaticDescriptors; - private Map quarkusConfigUnsupportedProperties; - - //All standard getters and setters generated by IDE: - - public Optional getDataSource() { - return dataSource; - } - - public void setDataSource(Optional dataSource) { - this.dataSource = dataSource; - } - - public String getPuName() { - return puName; - } - - public void setPuName(String puName) { - this.puName = puName; - } - - public MultiTenancyStrategy getMultitenancyStrategy() { - return multitenancyStrategy; - } - - public void setMultitenancyStrategy(MultiTenancyStrategy multitenancyStrategy) { - this.multitenancyStrategy = multitenancyStrategy; - } - - public List getXmlMappingBindings() { - return xmlMappingBindings; - } - - public void setXmlMappingBindings(List xmlMappingBindings) { - this.xmlMappingBindings = xmlMappingBindings; - } - - public boolean isReactive() { - return isReactive; - } - - public void setReactive(boolean reactive) { - isReactive = reactive; - } - - public boolean isFromPersistenceXml() { - return fromPersistenceXml; - } - - public void setFromPersistenceXml(boolean fromPersistenceXml) { - this.fromPersistenceXml = fromPersistenceXml; - } - - public String getPuProviderClassName() { - return puProviderClassName; - } - - public void setPuProviderClassName(String puProviderClassName) { - this.puProviderClassName = puProviderClassName; - } - - public boolean isPuUseQuotedIdentifiers() { - return puUseQuotedIdentifiers; - } - - public void setPuUseQuotedIdentifiers(boolean puUseQuotedIdentifiers) { - this.puUseQuotedIdentifiers = puUseQuotedIdentifiers; - } - - public PersistenceUnitTransactionType getPuTransactionType() { - return puTransactionType; - } - - public void setPuTransactionType(PersistenceUnitTransactionType puTransactionType) { - this.puTransactionType = puTransactionType; - } - - public ValidationMode getPuValidationMode() { - return puValidationMode; - } - - public void setPuValidationMode(ValidationMode puValidationMode) { - this.puValidationMode = puValidationMode; - } - - public SharedCacheMode getPuSharedCachemode() { - return puSharedCachemode; - } - - public void setPuSharedCachemode(SharedCacheMode puSharedCachemode) { - this.puSharedCachemode = puSharedCachemode; - } - - public List getPuManagedClassNames() { - return puManagedClassNames; - } - - public void setPuManagedClassNames(List puManagedClassNames) { - this.puManagedClassNames = puManagedClassNames; - } - - public Properties getPuProperties() { - return puProperties; - } - - public void setPuProperties(Properties puProperties) { - this.puProperties = puProperties; - } - - public List getIntegrationStaticDescriptors() { - return integrationStaticDescriptors; - } - - public void setIntegrationStaticDescriptors( - List integrationStaticDescriptors) { - this.integrationStaticDescriptors = integrationStaticDescriptors; - } - - public Map getQuarkusConfigUnsupportedProperties() { - return quarkusConfigUnsupportedProperties; - } - - public void setQuarkusConfigUnsupportedProperties(Map quarkusConfigUnsupportedProperties) { - this.quarkusConfigUnsupportedProperties = quarkusConfigUnsupportedProperties; - } - } - - public static final class Substitution implements ObjectSubstitution { - - @Override - public Serialized serialize(final QuarkusPersistenceUnitDefinition obj) { - final Serialized s = new Serialized(); - //First, fields from LightPersistenceXmlDescriptor: - s.setPuName(obj.actualHibernateDescriptor.getName()); - s.setPuProviderClassName(obj.actualHibernateDescriptor.getProviderClassName()); - s.setPuUseQuotedIdentifiers(obj.actualHibernateDescriptor.isUseQuotedIdentifiers()); - s.setPuTransactionType(obj.actualHibernateDescriptor.getTransactionType()); - s.setPuValidationMode(obj.actualHibernateDescriptor.getValidationMode()); - s.setPuSharedCachemode(obj.actualHibernateDescriptor.getSharedCacheMode()); - s.setPuManagedClassNames(obj.actualHibernateDescriptor.getManagedClassNames()); - s.setPuProperties(obj.actualHibernateDescriptor.getProperties()); - //Remaining fields of QuarkusPersistenceUnitDefinition - s.setDataSource(obj.getDataSource()); - s.setMultitenancyStrategy(obj.getMultitenancyStrategy()); - s.setXmlMappingBindings(obj.getXmlMappings()); - s.setQuarkusConfigUnsupportedProperties(obj.getQuarkusConfigUnsupportedProperties()); - s.setReactive(obj.isReactive); - s.setFromPersistenceXml(obj.isFromPersistenceXml()); - s.setIntegrationStaticDescriptors(obj.getIntegrationStaticDescriptors()); - return s; - } - - @Override - public QuarkusPersistenceUnitDefinition deserialize(Serialized obj) { - LightPersistenceXmlDescriptor xmlDescriptor = new LightPersistenceXmlDescriptor( - obj.puName, obj.puProviderClassName, obj.puUseQuotedIdentifiers, obj.puTransactionType, - obj.puValidationMode, obj.puSharedCachemode, obj.puManagedClassNames, obj.puProperties); - - return new QuarkusPersistenceUnitDefinition(xmlDescriptor, obj.getDataSource(), obj.getMultitenancyStrategy(), - obj.getXmlMappingBindings(), obj.getQuarkusConfigUnsupportedProperties(), - obj.isReactive(), obj.isFromPersistenceXml(), - obj.getIntegrationStaticDescriptors()); - } - } - } From 0bc9711a2713c294631628ad4ec4e5a5ce1196a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 2 Aug 2022 15:19:01 +0200 Subject: [PATCH 09/13] Propagate the "configuration name" of persistence unit throughout the build We'll need these at runtime in the next few commits. --- .../orm/deployment/HibernateOrmProcessor.java | 7 +++++-- .../PersistenceUnitDescriptorBuildItem.java | 17 +++++++++++++---- .../FastBootHibernatePersistenceProvider.java | 3 ++- .../orm/runtime/PersistenceUnitsHolder.java | 13 +++++++------ .../boot/QuarkusPersistenceUnitDefinition.java | 12 +++++++----- ...va => RuntimePersistenceUnitDescriptor.java} | 17 +++++++++++++---- .../deployment/HibernateReactiveProcessor.java | 1 + ...ootHibernateReactivePersistenceProvider.java | 5 +++-- 8 files changed, 51 insertions(+), 24 deletions(-) rename extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/{LightPersistenceXmlDescriptor.java => RuntimePersistenceUnitDescriptor.java} (88%) 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 37f574959a54b..5fca69b3dae9a 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 @@ -388,8 +388,10 @@ public void configurationDescriptorBuilding( // First produce the PUs having a persistence.xml: these are not reactive, as we don't allow using a persistence.xml for them. for (PersistenceXmlDescriptorBuildItem persistenceXmlDescriptorBuildItem : persistenceXmlDescriptors) { + ParsedPersistenceXmlDescriptor xmlDescriptor = persistenceXmlDescriptorBuildItem.getDescriptor(); persistenceUnitDescriptors - .produce(new PersistenceUnitDescriptorBuildItem(persistenceXmlDescriptorBuildItem.getDescriptor(), + .produce(new PersistenceUnitDescriptorBuildItem(xmlDescriptor, + xmlDescriptor.getName(), Optional.of(DataSourceUtil.DEFAULT_DATASOURCE_NAME), getMultiTenancyStrategy(Optional.ofNullable(persistenceXmlDescriptorBuildItem.getDescriptor() .getProperties().getProperty(AvailableSettings.MULTI_TENANT))), @@ -1165,7 +1167,8 @@ private static void producePersistenceUnitDescriptorFromConfig( String.valueOf(persistenceUnitConfig.discriminator.ignoreExplicitForJoined)); persistenceUnitDescriptors.produce( - new PersistenceUnitDescriptorBuildItem(descriptor, jdbcDataSource.map(JdbcDataSourceBuildItem::getName), + new PersistenceUnitDescriptorBuildItem(descriptor, descriptor.getName(), + jdbcDataSource.map(JdbcDataSourceBuildItem::getName), multiTenancyStrategy, persistenceUnitConfig.multitenantSchemaDatasource.orElse(null), xmlMappings, diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/PersistenceUnitDescriptorBuildItem.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/PersistenceUnitDescriptorBuildItem.java index 6a4f4291cd94c..6044d7711c1f5 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/PersistenceUnitDescriptorBuildItem.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/PersistenceUnitDescriptorBuildItem.java @@ -23,6 +23,11 @@ public final class PersistenceUnitDescriptorBuildItem extends MultiBuildItem { private final ParsedPersistenceXmlDescriptor descriptor; + + // The default PU in Hibernate Reactive is named "default-reactive" instead of "", + // but everything related to configuration (e.g. getAllPersistenceUnitConfigsAsMap() still + // use the name "", so we need to convert between those. + private final String configurationName; private final Optional dataSource; private final MultiTenancyStrategy multiTenancyStrategy; private final String multiTenancySchemaDataSource; @@ -31,20 +36,23 @@ public final class PersistenceUnitDescriptorBuildItem extends MultiBuildItem { private final boolean isReactive; private final boolean fromPersistenceXml; - public PersistenceUnitDescriptorBuildItem(ParsedPersistenceXmlDescriptor descriptor, + public PersistenceUnitDescriptorBuildItem(ParsedPersistenceXmlDescriptor descriptor, String configurationName, List xmlMappings, Map quarkusConfigUnsupportedProperties, boolean isReactive, boolean fromPersistenceXml) { - this(descriptor, Optional.of(DataSourceUtil.DEFAULT_DATASOURCE_NAME), MultiTenancyStrategy.NONE, null, + this(descriptor, configurationName, + Optional.of(DataSourceUtil.DEFAULT_DATASOURCE_NAME), MultiTenancyStrategy.NONE, null, xmlMappings, quarkusConfigUnsupportedProperties, isReactive, fromPersistenceXml); } - public PersistenceUnitDescriptorBuildItem(ParsedPersistenceXmlDescriptor descriptor, Optional dataSource, + public PersistenceUnitDescriptorBuildItem(ParsedPersistenceXmlDescriptor descriptor, String configurationName, + Optional dataSource, MultiTenancyStrategy multiTenancyStrategy, String multiTenancySchemaDataSource, List xmlMappings, Map quarkusConfigUnsupportedProperties, boolean isReactive, boolean fromPersistenceXml) { this.descriptor = descriptor; + this.configurationName = configurationName; this.dataSource = dataSource; this.multiTenancyStrategy = multiTenancyStrategy; this.multiTenancySchemaDataSource = multiTenancySchemaDataSource; @@ -84,7 +92,8 @@ public boolean hasXmlMappings() { public QuarkusPersistenceUnitDefinition asOutputPersistenceUnitDefinition( List integrationStaticDescriptors) { - return new QuarkusPersistenceUnitDefinition(descriptor, dataSource, multiTenancyStrategy, xmlMappings, + return new QuarkusPersistenceUnitDefinition(descriptor, configurationName, dataSource, multiTenancyStrategy, + xmlMappings, quarkusConfigUnsupportedProperties, isReactive, fromPersistenceXml, integrationStaticDescriptors); } } 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 bb848c38c8bc1..a8c11b0bfe84d 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 @@ -30,6 +30,7 @@ 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; import io.quarkus.hibernate.orm.runtime.boot.registry.PreconfiguredServiceRegistryBuilder; import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeDescriptor; import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; @@ -136,7 +137,7 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String verifyProperties(properties); // These are pre-parsed during image generation: - final List units = PersistenceUnitsHolder.getPersistenceUnitDescriptors(); + final List units = PersistenceUnitsHolder.getPersistenceUnitDescriptors(); log.debugf("Located %s persistence units; checking each", units.size()); diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceUnitsHolder.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceUnitsHolder.java index 32e219ba5b4dd..d550073b928bd 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceUnitsHolder.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/PersistenceUnitsHolder.java @@ -8,10 +8,10 @@ import org.hibernate.boot.archive.scan.spi.Scanner; import org.hibernate.integrator.spi.Integrator; -import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; import io.quarkus.hibernate.orm.runtime.boot.FastBootMetadataBuilder; import io.quarkus.hibernate.orm.runtime.boot.QuarkusPersistenceUnitDefinition; +import io.quarkus.hibernate.orm.runtime.boot.RuntimePersistenceUnitDescriptor; import io.quarkus.hibernate.orm.runtime.proxies.PreGeneratedProxies; import io.quarkus.hibernate.orm.runtime.recording.RecordedState; @@ -36,14 +36,14 @@ public final class PersistenceUnitsHolder { static void initializeJpa(List puDefinitions, Scanner scanner, Collection> additionalIntegrators, PreGeneratedProxies preGeneratedProxies) { - final List units = convertPersistenceUnits(puDefinitions); + final List units = convertPersistenceUnits(puDefinitions); final Map metadata = constructMetadataAdvance(puDefinitions, scanner, additionalIntegrators, preGeneratedProxies); persistenceUnits = new PersistenceUnits(units, metadata); } - public static List getPersistenceUnitDescriptors() { + public static List getPersistenceUnitDescriptors() { checkJPAInitialization(); return persistenceUnits.units; } @@ -57,7 +57,7 @@ public static RecordedState popRecordedState(String persistenceUnitName) { return persistenceUnits.recordedStates.remove(key); } - private static List convertPersistenceUnits( + private static List convertPersistenceUnits( final List parsedPersistenceXmlDescriptors) { return parsedPersistenceXmlDescriptors.stream().map(QuarkusPersistenceUnitDefinition::getActualHibernateDescriptor) .collect(Collectors.toList()); @@ -103,11 +103,12 @@ public static RecordedState createMetadata(QuarkusPersistenceUnitDefinition unit private static class PersistenceUnits { - private final List units; + private final List units; private final Map recordedStates; - public PersistenceUnits(final List units, final Map recordedStates) { + public PersistenceUnits(final List units, + final Map recordedStates) { this.units = units; this.recordedStates = recordedStates; } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java index 4c6a3c27907ce..2b6b1c4901216 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/QuarkusPersistenceUnitDefinition.java @@ -18,7 +18,7 @@ */ public final class QuarkusPersistenceUnitDefinition { - private final LightPersistenceXmlDescriptor actualHibernateDescriptor; + private final RuntimePersistenceUnitDescriptor actualHibernateDescriptor; private final Optional dataSource; private final MultiTenancyStrategy multitenancyStrategy; private final List xmlMappings; @@ -27,14 +27,16 @@ public final class QuarkusPersistenceUnitDefinition { private final List integrationStaticDescriptors; private final Map quarkusConfigUnsupportedProperties; - public QuarkusPersistenceUnitDefinition(PersistenceUnitDescriptor persistenceUnitDescriptor, Optional dataSource, + public QuarkusPersistenceUnitDefinition(PersistenceUnitDescriptor persistenceUnitDescriptor, + String configurationName, Optional dataSource, MultiTenancyStrategy multitenancyStrategy, List xmlMappings, Map quarkusConfigUnsupportedProperties, boolean isReactive, boolean fromPersistenceXml, List integrationStaticDescriptors) { Objects.requireNonNull(persistenceUnitDescriptor); Objects.requireNonNull(multitenancyStrategy); - this.actualHibernateDescriptor = LightPersistenceXmlDescriptor.validateAndReadFrom(persistenceUnitDescriptor); + this.actualHibernateDescriptor = RuntimePersistenceUnitDescriptor.validateAndReadFrom(persistenceUnitDescriptor, + configurationName); this.dataSource = dataSource; this.multitenancyStrategy = multitenancyStrategy; this.xmlMappings = xmlMappings; @@ -45,7 +47,7 @@ public QuarkusPersistenceUnitDefinition(PersistenceUnitDescriptor persistenceUni } @RecordableConstructor - public QuarkusPersistenceUnitDefinition(LightPersistenceXmlDescriptor actualHibernateDescriptor, + public QuarkusPersistenceUnitDefinition(RuntimePersistenceUnitDescriptor actualHibernateDescriptor, Optional dataSource, MultiTenancyStrategy multitenancyStrategy, List xmlMappings, @@ -66,7 +68,7 @@ public QuarkusPersistenceUnitDefinition(LightPersistenceXmlDescriptor actualHibe this.integrationStaticDescriptors = integrationStaticDescriptors; } - public PersistenceUnitDescriptor getActualHibernateDescriptor() { + public RuntimePersistenceUnitDescriptor getActualHibernateDescriptor() { return actualHibernateDescriptor; } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/LightPersistenceXmlDescriptor.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/RuntimePersistenceUnitDescriptor.java similarity index 88% rename from extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/LightPersistenceXmlDescriptor.java rename to extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/RuntimePersistenceUnitDescriptor.java index 36f03601cee10..39b74a279cf92 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/LightPersistenceXmlDescriptor.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/RuntimePersistenceUnitDescriptor.java @@ -15,9 +15,10 @@ import io.quarkus.runtime.annotations.RecordableConstructor; -public final class LightPersistenceXmlDescriptor implements PersistenceUnitDescriptor { +public final class RuntimePersistenceUnitDescriptor implements PersistenceUnitDescriptor { private final String name; + private final String configurationName; private final String providerClassName; private final boolean useQuotedIdentifiers; private final PersistenceUnitTransactionType transactionType; @@ -33,11 +34,13 @@ public final class LightPersistenceXmlDescriptor implements PersistenceUnitDescr */ @Deprecated @RecordableConstructor - public LightPersistenceXmlDescriptor(String name, String providerClassName, boolean useQuotedIdentifiers, + public RuntimePersistenceUnitDescriptor(String name, String configurationName, + String providerClassName, boolean useQuotedIdentifiers, PersistenceUnitTransactionType transactionType, ValidationMode validationMode, SharedCacheMode sharedCacheMode, List managedClassNames, Properties properties) { this.name = name; + this.configurationName = configurationName; this.providerClassName = providerClassName; this.useQuotedIdentifiers = useQuotedIdentifiers; this.transactionType = transactionType; @@ -52,14 +55,16 @@ public LightPersistenceXmlDescriptor(String name, String providerClassName, bool * several options that Quarkus does not support are not set. * * @param toClone the descriptor to clone + * @param configurationName the name of this PU in Quarkus configuration * @return a new instance of LightPersistenceXmlDescriptor * @throws UnsupportedOperationException on unsupported configurations */ @SuppressWarnings("deprecated") - public static LightPersistenceXmlDescriptor validateAndReadFrom(PersistenceUnitDescriptor toClone) { + public static RuntimePersistenceUnitDescriptor validateAndReadFrom(PersistenceUnitDescriptor toClone, + String configurationName) { Objects.requireNonNull(toClone); verifyIgnoredFields(toClone); - return new LightPersistenceXmlDescriptor(toClone.getName(), toClone.getProviderClassName(), + return new RuntimePersistenceUnitDescriptor(toClone.getName(), configurationName, toClone.getProviderClassName(), toClone.isUseQuotedIdentifiers(), toClone.getTransactionType(), toClone.getValidationMode(), toClone.getSharedCacheMode(), Collections.unmodifiableList(toClone.getManagedClassNames()), toClone.getProperties()); @@ -75,6 +80,10 @@ public String getName() { return name; } + public String getConfigurationName() { + return configurationName; + } + @Override public String getProviderClassName() { return providerClassName; diff --git a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java index 53a0e7e1c400b..7743124459829 100644 --- a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java +++ b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java @@ -163,6 +163,7 @@ public void buildReactivePersistenceUnit( // - we don't support starting Hibernate Reactive from a persistence.xml // - we don't support Hibernate Envers with Hibernate Reactive persistenceUnitDescriptors.produce(new PersistenceUnitDescriptorBuildItem(reactivePU, + PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, jpaModel.getXmlMappings(reactivePU.getName()), persistenceUnitConfig.unsupportedProperties, true, false)); diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java index 7595c8c5eeffa..92de27a8ecca2 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java @@ -36,6 +36,7 @@ import io.quarkus.hibernate.orm.runtime.PersistenceUnitsHolder; import io.quarkus.hibernate.orm.runtime.RuntimeSettings; import io.quarkus.hibernate.orm.runtime.RuntimeSettings.Builder; +import io.quarkus.hibernate.orm.runtime.boot.RuntimePersistenceUnitDescriptor; import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeDescriptor; import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; import io.quarkus.hibernate.orm.runtime.recording.PrevalidatedQuarkusMetadata; @@ -77,7 +78,7 @@ public EntityManagerFactory createEntityManagerFactory(String emName, Map proper properties = new HashMap(); try { // These are pre-parsed during image generation: - final List units = PersistenceUnitsHolder.getPersistenceUnitDescriptors(); + final List units = PersistenceUnitsHolder.getPersistenceUnitDescriptors(); for (PersistenceUnitDescriptor unit : units) { //if the provider is not set, don't use it as people might want to use Hibernate ORM @@ -106,7 +107,7 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String verifyProperties(properties); // These are pre-parsed during image generation: - final List units = PersistenceUnitsHolder.getPersistenceUnitDescriptors(); + final List units = PersistenceUnitsHolder.getPersistenceUnitDescriptors(); log.debugf("Located %s persistence units; checking each", units.size()); From 56ab22e70cee3a6abd95b345490aa65728f37d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 29 Jul 2022 11:50:19 +0200 Subject: [PATCH 10/13] Add runtime configuration property quarkus.hibernate-orm.active --- .../deployment/HibernateOrmCdiProcessor.java | 5 +- .../orm/deployment/HibernateOrmConfig.java | 3 +- .../HibernateOrmDisabledProcessor.java | 23 ++++ .../orm/deployment/HibernateOrmProcessor.java | 5 +- .../ConfigActiveFalseAndEntityTest.java | 91 +++++++++++++ .../ConfigEnabledFalseAndActiveTrueTest.java | 30 +++++ .../ConfigEnabledFalseAndEntityTest.java | 6 +- .../FastBootHibernatePersistenceProvider.java | 8 +- .../runtime/HibernateOrmDisabledRecorder.java | 32 +++++ .../runtime/HibernateOrmRuntimeConfig.java | 4 + ...ernateOrmRuntimeConfigPersistenceUnit.java | 15 +++ .../hibernate/orm/runtime/JPAConfig.java | 44 +++++-- .../ConfigActiveFalseAndEntityTest.java | 121 ++++++++++++++++++ .../ConfigEnabledFalseAndActiveTrueTest.java | 30 +++++ .../ConfigEnabledFalseAndEntityTest.java | 24 +++- ...tHibernateReactivePersistenceProvider.java | 21 ++- .../ReactiveSessionFactoryProducer.java | 11 ++ ...rmActiveFalseAndNamedPuActiveTrueTest.java | 56 ++++++++ ...DevConsoleHibernateOrmActiveFalseTest.java | 45 +++++++ .../devconsole/namedpu/MyNamedPuEntity.java | 14 ++ 20 files changed, 559 insertions(+), 29 deletions(-) create mode 100644 extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmDisabledProcessor.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigActiveFalseAndEntityTest.java create mode 100644 extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigEnabledFalseAndActiveTrueTest.java create mode 100644 extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmDisabledRecorder.java create mode 100644 extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigActiveFalseAndEntityTest.java create mode 100644 extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigEnabledFalseAndActiveTrueTest.java create mode 100644 integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleHibernateOrmActiveFalseAndNamedPuActiveTrueTest.java create mode 100644 integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleHibernateOrmActiveFalseTest.java create mode 100644 integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/namedpu/MyNamedPuEntity.java diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java index 94aaf77a05e94..d46d50e8183ec 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmCdiProcessor.java @@ -104,7 +104,9 @@ public void transform(TransformationContext transformationContext) { return new AnnotationsTransformerBuildItem(transformer); } - @Record(ExecutionTime.STATIC_INIT) + // These beans must be initialized at runtime because their initialization + // depends on runtime configuration (to activate/deactivate a persistence unit) + @Record(ExecutionTime.RUNTIME_INIT) @BuildStep void generateDataSourceBeans(HibernateOrmRecorder recorder, List persistenceUnitDescriptors, @@ -201,6 +203,7 @@ private static SyntheticBeanBuildItem createSyntheticBean(String persistence // See https://github.com/quarkusio/quarkus/issues/16437 .scope(ApplicationScoped.class) .unremovable() + .setRuntimeInit() .supplier(supplier); for (DotName exposedType : allExposedTypes) { diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java index f7280638b1d74..f32da2be468d5 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfig.java @@ -18,7 +18,8 @@ public class HibernateOrmConfig { * Whether Hibernate ORM is enabled during the build. * * If Hibernate ORM is disabled during the build, all processing related to Hibernate ORM will be skipped, - * but it will not be possible to use Hibernate ORM at runtime. + * but it will not be possible to activate Hibernate ORM at runtime: + * `quarkus.hibernate-orm.active` will default to `false` and setting it to `true` will lead to an error. * * @asciidoclet */ diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmDisabledProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmDisabledProcessor.java new file mode 100644 index 0000000000000..f4530ca6ef75d --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmDisabledProcessor.java @@ -0,0 +1,23 @@ +package io.quarkus.hibernate.orm.deployment; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.hibernate.orm.runtime.HibernateOrmDisabledRecorder; +import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig; + +@BuildSteps(onlyIfNot = HibernateOrmEnabled.class) +class HibernateOrmDisabledProcessor { + + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + public void disableHibernateOrm(HibernateOrmDisabledRecorder recorder, HibernateOrmRuntimeConfig runtimeConfig) { + // The disabling itself is done through conditions on build steps (see uses of HibernateOrmEnabled.class) + + // We still want to check that nobody tries to set quarkus.hibernate-orm.active = true at runtime + // if Hibernate ORM is disabled, though: + recorder.checkNoExplicitActiveTrue(runtimeConfig); + } + +} 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 5fca69b3dae9a..7b74c27c71543 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 @@ -72,6 +72,7 @@ import io.quarkus.arc.deployment.RecorderBeanInitializedBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem.ExtendedBeanConfigurator; +import io.quarkus.arc.deployment.SyntheticBeansRuntimeInitBuildItem; import io.quarkus.arc.deployment.UnremovableBeanBuildItem; import io.quarkus.arc.deployment.UnremovableBeanBuildItem.BeanTypeExclusion; import io.quarkus.arc.deployment.staticmethods.InterceptedStaticMethodsTransformersRegisteredBuildItem; @@ -698,8 +699,9 @@ public HibernateModelClassCandidatesForFieldAccessBuildItem candidatesForFieldAc } @BuildStep - @Record(STATIC_INIT) + @Record(RUNTIME_INIT) public void build(HibernateOrmRecorder recorder, HibernateOrmConfig hibernateOrmConfig, + HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig, BuildProducer jpaModelPersistenceUnitMapping, List descriptors, JpaModelBuildItem jpaModel) throws Exception { @@ -733,6 +735,7 @@ public PersistenceProviderSetUpBuildItem setupPersistenceProvider(HibernateOrmRe } @BuildStep + @Consume(SyntheticBeansRuntimeInitBuildItem.class) @Record(RUNTIME_INIT) public ServiceStartBuildItem startPersistenceUnits(HibernateOrmRecorder recorder, BeanContainerBuildItem beanContainer, List dataSourcesConfigured, diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigActiveFalseAndEntityTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigActiveFalseAndEntityTest.java new file mode 100644 index 0000000000000..adba4fcda687f --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigActiveFalseAndEntityTest.java @@ -0,0 +1,91 @@ +package io.quarkus.hibernate.orm.config; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import javax.enterprise.context.control.ActivateRequestContext; +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.test.QuarkusUnitTest; + +public class ConfigActiveFalseAndEntityTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar.addClass(MyEntity.class)) + .withConfigurationResource("application.properties") + .overrideConfigKey("quarkus.hibernate-orm.active", "false"); + + @Test + public void entityManagerFactory() { + EntityManagerFactory entityManagerFactory = Arc.container().instance(EntityManagerFactory.class).get(); + + // The bean is always available to be injected during static init + // since we don't know whether ORM will be active at runtime. + // So the bean cannot be null. + assertThat(entityManagerFactory).isNotNull(); + // However, any attempt to use it at runtime will fail. + assertThatThrownBy(entityManagerFactory::getMetamodel) + .isInstanceOf(IllegalStateException.class) + .hasMessageContainingAll( + "Cannot retrieve the EntityManagerFactory/SessionFactory for persistence unit ", + "Hibernate ORM was deactivated through configuration properties"); + } + + @Test + public void sessionFactory() { + SessionFactory sessionFactory = Arc.container().instance(SessionFactory.class).get(); + + // The bean is always available to be injected during static init + // since we don't know whether ORM will be active at runtime. + // So the bean cannot be null. + assertThat(sessionFactory).isNotNull(); + // However, any attempt to use it at runtime will fail. + assertThatThrownBy(sessionFactory::getMetamodel) + .isInstanceOf(IllegalStateException.class) + .hasMessageContainingAll( + "Cannot retrieve the EntityManagerFactory/SessionFactory for persistence unit ", + "Hibernate ORM was deactivated through configuration properties"); + } + + @Test + @ActivateRequestContext + public void entityManager() { + EntityManager entityManager = Arc.container().instance(EntityManager.class).get(); + + // The bean is always available to be injected during static init + // since we don't know whether ORM will be active at runtime. + // So the bean cannot be null. + assertThat(entityManager).isNotNull(); + // However, any attempt to use it at runtime will fail. + assertThatThrownBy(() -> entityManager.find(MyEntity.class, 0L)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContainingAll( + "Cannot retrieve the EntityManagerFactory/SessionFactory for persistence unit ", + "Hibernate ORM was deactivated through configuration properties"); + } + + @Test + @ActivateRequestContext + public void session() { + Session session = Arc.container().instance(Session.class).get(); + + // The bean is always available to be injected during static init + // since we don't know whether ORM will be active at runtime. + // So the bean cannot be null. + assertThat(session).isNotNull(); + // However, any attempt to use it at runtime will fail. + assertThatThrownBy(() -> session.find(MyEntity.class, 0L)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContainingAll( + "Cannot retrieve the EntityManagerFactory/SessionFactory for persistence unit ", + "Hibernate ORM was deactivated through configuration properties"); + } +} diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigEnabledFalseAndActiveTrueTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigEnabledFalseAndActiveTrueTest.java new file mode 100644 index 0000000000000..635243010156e --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigEnabledFalseAndActiveTrueTest.java @@ -0,0 +1,30 @@ +package io.quarkus.hibernate.orm.config; + +import static org.assertj.core.api.Assertions.assertThat; + +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 ConfigEnabledFalseAndActiveTrueTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar.addClass(MyEntity.class)) + .withConfigurationResource("application.properties") + .overrideConfigKey("quarkus.hibernate-orm.enabled", "false") + .overrideConfigKey("quarkus.hibernate-orm.active", "true") + .assertException(throwable -> assertThat(throwable) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining( + "Hibernate ORM activated explicitly for persistence unit '', but the Hibernate ORM extension was disabled at build time", + "If you want Hibernate ORM to be active at runtime, you must set 'quarkus.hibernate-orm.enabled' to 'true' at build time", + "If you don't want Hibernate ORM to be active at runtime, you must leave 'quarkus.hibernate-orm.active' unset or set it to 'false'")); + + @Test + public void test() { + // Startup will fail + } +} diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigEnabledFalseAndEntityTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigEnabledFalseAndEntityTest.java index a4f41f7d473ce..f4ca7a7979d2a 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigEnabledFalseAndEntityTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/config/ConfigEnabledFalseAndEntityTest.java @@ -8,8 +8,6 @@ import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -19,8 +17,8 @@ public class ConfigEnabledFalseAndEntityTest { @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest().setArchiveProducer( - () -> ShrinkWrap.create(JavaArchive.class).addClass(MyEntity.class)) + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar.addClass(MyEntity.class)) .withConfigurationResource("application.properties") // This should disable Hibernate ORM even if there is an entity .overrideConfigKey("quarkus.hibernate-orm.enabled", "false"); 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 a8c11b0bfe84d..c26e77171cf2b 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 @@ -149,7 +149,7 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String Map puConfigMap = hibernateOrmRuntimeConfig .getAllPersistenceUnitConfigsAsMap(); - for (PersistenceUnitDescriptor persistenceUnit : units) { + for (RuntimePersistenceUnitDescriptor persistenceUnit : units) { log.debugf( "Checking persistence-unit [name=%s, explicit-provider=%s] against incoming persistence unit name [%s]", persistenceUnit.getName(), persistenceUnit.getProviderClassName(), persistenceUnitName); @@ -174,8 +174,12 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String "Attempting to boot a blocking Hibernate ORM instance on a reactive RecordedState"); } final PrevalidatedQuarkusMetadata metadata = recordedState.getMetadata(); - var puConfig = puConfigMap.getOrDefault(persistenceUnitName, + var puConfig = puConfigMap.getOrDefault(persistenceUnit.getConfigurationName(), new HibernateOrmRuntimeConfigPersistenceUnit()); + if (puConfig.active.isPresent() && !puConfig.active.get()) { + throw new IllegalStateException( + "Attempting to boot a deactivated Hibernate ORM persistence unit"); + } RuntimeSettings runtimeSettings = buildRuntimeSettings(persistenceUnitName, recordedState, puConfig); StandardServiceRegistry standardServiceRegistry = rewireMetadataAndExtractServiceRegistry(runtimeSettings, diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmDisabledRecorder.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmDisabledRecorder.java new file mode 100644 index 0000000000000..a60b25ffc1216 --- /dev/null +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmDisabledRecorder.java @@ -0,0 +1,32 @@ +package io.quarkus.hibernate.orm.runtime; + +import java.util.Set; + +import io.quarkus.runtime.annotations.Recorder; +import io.quarkus.runtime.configuration.ConfigurationException; + +@Recorder +public class HibernateOrmDisabledRecorder { + + public void checkNoExplicitActiveTrue(HibernateOrmRuntimeConfig runtimeConfig) { + for (var entry : runtimeConfig.getAllPersistenceUnitConfigsAsMap().entrySet()) { + var config = entry.getValue(); + if (config.active.isPresent() && config.active.get()) { + var puName = entry.getKey(); + String enabledPropertyKey = HibernateOrmRuntimeConfig.extensionPropertyKey("enabled"); + String activePropertyKey = HibernateOrmRuntimeConfig.puPropertyKey(puName, "active"); + throw new ConfigurationException( + "Hibernate ORM activated explicitly for persistence unit '" + puName + + "', but the Hibernate ORM extension was disabled at build time." + + " If you want Hibernate ORM to be active for this persistence unit, you must set '" + + enabledPropertyKey + + "' to 'true' at build time." + + " If you don't want Hibernate ORM to be active for this persistence unit, you must leave '" + + activePropertyKey + + "' unset or set it to 'false'.", + Set.of(enabledPropertyKey, activePropertyKey)); + } + } + } + +} diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfig.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfig.java index e7c4483ffc011..5392edca301b1 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfig.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfig.java @@ -35,6 +35,10 @@ public Map getAllPersistenceUn return map; } + public static String extensionPropertyKey(String radical) { + return "quarkus.hibernate-orm." + radical; + } + public static String puPropertyKey(String puName, String radical) { String prefix = PersistenceUnitUtil.isDefaultPersistenceUnit(puName) ? "quarkus.hibernate-orm." diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java index 341bcf2ebf5bd..cda963fac09ac 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java @@ -14,6 +14,21 @@ @ConfigGroup public class HibernateOrmRuntimeConfigPersistenceUnit { + /** + * Whether this persistence unit should be active at runtime. + * + * If the persistence unit is not active, it won't start with the application, + * and accessing the corresponding EntityManagerFactory/EntityManager or SessionFactory/Session + * will not be possible. + * + * Note that if Hibernate ORM is disabled (i.e. `quarkus.hibernate-orm.enabled` is set to `false`), + * all persistence units are deactivated, and setting this property to `true` will fail. + * + * @asciidoclet + */ + @ConfigItem(defaultValueDocumentation = "`true` if Hibernate ORM is enabled; `false` otherwise") + public Optional active = Optional.empty(); + /** * Database related configuration. */ 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 53a5a098ae480..9b1475d902663 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 @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -18,24 +19,34 @@ import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; -import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; import org.jboss.logging.Logger; +import io.quarkus.hibernate.orm.runtime.boot.RuntimePersistenceUnitDescriptor; + @Singleton public class JPAConfig { private static final Logger LOGGER = Logger.getLogger(JPAConfig.class.getName()); - private final Map persistenceUnits; + private final Map persistenceUnits = new HashMap<>(); + private final Set deactivatedPersistenceUnitNames = new HashSet<>(); @Inject - public JPAConfig() { - Map persistenceUnitsBuilder = new HashMap<>(); - for (PersistenceUnitDescriptor descriptor : PersistenceUnitsHolder.getPersistenceUnitDescriptors()) { + public JPAConfig(HibernateOrmRuntimeConfig hibernateOrmRuntimeConfig) { + Map puConfigMap = hibernateOrmRuntimeConfig + .getAllPersistenceUnitConfigsAsMap(); + for (RuntimePersistenceUnitDescriptor descriptor : PersistenceUnitsHolder.getPersistenceUnitDescriptors()) { String puName = descriptor.getName(); - persistenceUnitsBuilder.put(puName, new LazyPersistenceUnit(puName)); + var puConfig = puConfigMap.getOrDefault(descriptor.getConfigurationName(), + new HibernateOrmRuntimeConfigPersistenceUnit()); + if (puConfig.active.isPresent() && !puConfig.active.get()) { + LOGGER.infof("Hibernate ORM persistence unit '%s' was deactivated through configuration properties", + puName); + deactivatedPersistenceUnitNames.add(puName); + } else { + persistenceUnits.put(puName, new LazyPersistenceUnit(puName)); + } } - this.persistenceUnits = persistenceUnitsBuilder; } void startAll() { @@ -82,6 +93,12 @@ public EntityManagerFactory getEntityManagerFactory(String unitName) { } if (lazyPersistenceUnit == null) { + if (deactivatedPersistenceUnitNames.contains(unitName)) { + throw new IllegalStateException( + "Cannot retrieve the EntityManagerFactory/SessionFactory for persistence unit " + + unitName + + ": Hibernate ORM was deactivated through configuration properties"); + } throw new IllegalArgumentException( String.format(Locale.ROOT, "Unable to find an EntityManagerFactory for persistence unit '%s'", unitName)); } @@ -90,14 +107,23 @@ public EntityManagerFactory getEntityManagerFactory(String unitName) { } /** - * Returns the registered persistence units. + * Returns the registered, active persistence units. * - * @return Set containing the names of all registered persistence units. + * @return Set containing the names of all registered, actives persistence units. */ public Set getPersistenceUnits() { return persistenceUnits.keySet(); } + /** + * Returns the name of persistence units that were deactivated through configuration properties. + * + * @return Set containing the names of all persistence units that were deactivated through configuration properties. + */ + public Set getDeactivatedPersistenceUnitNames() { + return deactivatedPersistenceUnitNames; + } + /** * Need to shut down all instances of Hibernate ORM before the actual destroy event, * as it might need to use the datasources during shutdown. diff --git a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigActiveFalseAndEntityTest.java b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigActiveFalseAndEntityTest.java new file mode 100644 index 0000000000000..443fd3ce1f2c3 --- /dev/null +++ b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigActiveFalseAndEntityTest.java @@ -0,0 +1,121 @@ +package io.quarkus.hibernate.reactive.config; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +import javax.persistence.EntityManagerFactory; + +import org.hibernate.SessionFactory; +import org.hibernate.reactive.mutiny.Mutiny; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.test.QuarkusUnitTest; +import io.vertx.core.Vertx; +import io.vertx.core.impl.ContextInternal; + +public class ConfigActiveFalseAndEntityTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar.addClass(MyEntity.class)) + .withConfigurationResource("application.properties") + .overrideConfigKey("quarkus.hibernate-orm.active", "false"); + + @Test + public void entityManagerFactory() { + EntityManagerFactory entityManagerFactory = Arc.container().instance(EntityManagerFactory.class).get(); + + // The bean is always available to be injected during static init + // since we don't know whether Hibernate Reactive will be active at runtime. + // So the bean cannot be null. + assertThat(entityManagerFactory).isNotNull(); + // However, any attempt to use it at runtime will fail. + assertThatThrownBy(entityManagerFactory::getMetamodel) + .isInstanceOf(IllegalStateException.class) + .hasMessageContainingAll( + "Cannot retrieve the EntityManagerFactory/SessionFactory for persistence unit default-reactive", + "Hibernate ORM was deactivated through configuration properties"); + } + + @Test + public void sessionFactory() { + SessionFactory sessionFactory = Arc.container().instance(SessionFactory.class).get(); + + // The bean is always available to be injected during static init + // since we don't know whether Hibernate Reactive will be active at runtime. + // So the bean cannot be null. + assertThat(sessionFactory).isNotNull(); + // However, any attempt to use it at runtime will fail. + assertThatThrownBy(sessionFactory::getMetamodel) + .isInstanceOf(IllegalStateException.class) + .hasMessageContainingAll( + "Cannot retrieve the EntityManagerFactory/SessionFactory for persistence unit default-reactive", + "Hibernate ORM was deactivated through configuration properties"); + } + + @Test + public void mutinySessionFactory() { + Mutiny.SessionFactory sessionFactory = Arc.container().instance(Mutiny.SessionFactory.class).get(); + + // The bean is always available to be injected during static init + // since we don't know whether Hibernate Reactive will be active at runtime. + // So the bean cannot be null. + assertThat(sessionFactory).isNotNull(); + // However, any attempt to use it at runtime will fail. + assertThatThrownBy(sessionFactory::getMetamodel) + .isInstanceOf(IllegalStateException.class) + .hasMessageContainingAll( + "Cannot retrieve the Mutiny.SessionFactory for persistence unit default-reactive", + "Hibernate Reactive was deactivated through configuration properties"); + } + + @Test + public void mutinySession() { + Mutiny.Session session = Arc.container().instance(Mutiny.Session.class).get(); + + // The bean is always available to be injected during static init + // since we don't know whether Hibernate Reactive will be active at runtime. + // So the bean cannot be null. + assertThat(session).isNotNull(); + // However, any attempt to use it at runtime will fail. + assertThatThrownBy(() -> runInVertxAndJoin(() -> { + try { + Arc.container().requestContext().activate(); + return session.find(MyEntity.class, 0L); + } finally { + Arc.container().requestContext().deactivate(); + } + })) + .cause() + .isInstanceOf(IllegalStateException.class) + .hasMessageContainingAll("Cannot retrieve the Mutiny.SessionFactory for persistence unit default-reactive", + "Hibernate Reactive was deactivated through configuration properties"); + } + + private T runInVertxAndJoin(Supplier action) { + CompletableFuture future = new CompletableFuture<>(); + runInVertx(() -> { + try { + future.complete(action.get()); + } catch (Throwable t) { + future.completeExceptionally(t); + } + }); + return future.join(); + } + + // Copied from org.hibernate.reactive.context.impl.VertxContext.execute. + // Unfortunately we can't use that class here, so we need to duplicate a few lines of code. + // This seems to be the simplest way to run code in Vertx from a test... + private void runInVertx(Runnable action) { + final io.vertx.core.Context newContext = Arc.container().instance(Vertx.class).get().getOrCreateContext(); + ContextInternal newContextInternal = (ContextInternal) newContext; + final ContextInternal duplicate = newContextInternal.duplicate(); + duplicate.runOnContext(ignored -> action.run()); + } +} diff --git a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigEnabledFalseAndActiveTrueTest.java b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigEnabledFalseAndActiveTrueTest.java new file mode 100644 index 0000000000000..b2f3f97de9728 --- /dev/null +++ b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigEnabledFalseAndActiveTrueTest.java @@ -0,0 +1,30 @@ +package io.quarkus.hibernate.reactive.config; + +import static org.assertj.core.api.Assertions.assertThat; + +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 ConfigEnabledFalseAndActiveTrueTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar.addClass(MyEntity.class)) + .withConfigurationResource("application.properties") + .overrideConfigKey("quarkus.hibernate-orm.enabled", "false") + .overrideConfigKey("quarkus.hibernate-orm.active", "true") + .assertException(throwable -> assertThat(throwable) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining( + "Hibernate ORM activated explicitly for persistence unit '', but the Hibernate ORM extension was disabled at build time", + "If you want Hibernate ORM to be active for this persistence unit, you must set 'quarkus.hibernate-orm.enabled' to 'true' at build time", + "If you don't want Hibernate ORM to be active for this persistence unit, you must leave 'quarkus.hibernate-orm.active' unset or set it to 'false'")); + + @Test + public void test() { + // Startup will fail + } +} diff --git a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigEnabledFalseAndEntityTest.java b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigEnabledFalseAndEntityTest.java index 5e28603b0dec2..7e037649319d2 100644 --- a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigEnabledFalseAndEntityTest.java +++ b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/config/ConfigEnabledFalseAndEntityTest.java @@ -3,10 +3,10 @@ import static org.assertj.core.api.Assertions.assertThat; import javax.enterprise.context.control.ActivateRequestContext; +import javax.persistence.EntityManagerFactory; +import org.hibernate.SessionFactory; import org.hibernate.reactive.mutiny.Mutiny; -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -16,14 +16,28 @@ public class ConfigEnabledFalseAndEntityTest { @RegisterExtension - static final QuarkusUnitTest config = new QuarkusUnitTest().setArchiveProducer( - () -> ShrinkWrap.create(JavaArchive.class).addClass(MyEntity.class)) + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar.addClass(MyEntity.class)) .withConfigurationResource("application.properties") // This should disable Hibernate Reactive even if there is an entity .overrideConfigKey("quarkus.hibernate-orm.enabled", "false"); + @Test + public void entityManagerFactory() { + // The bean is not defined during static init, so it's null. + assertThat(Arc.container().instance(EntityManagerFactory.class).get()) + .isNull(); + } + @Test public void sessionFactory() { + // The bean is not defined during static init, so it's null. + assertThat(Arc.container().instance(SessionFactory.class).get()) + .isNull(); + } + + @Test + public void mutinySessionFactory() { // The bean is not defined during static init, so it's null. assertThat(Arc.container().instance(Mutiny.SessionFactory.class).get()) .isNull(); @@ -31,7 +45,7 @@ public void sessionFactory() { @Test @ActivateRequestContext - public void session() { + public void mutinySession() { // The bean is not defined during static init, so it's null. assertThat(Arc.container().instance(Mutiny.Session.class).get()) .isNull(); diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java index 92de27a8ecca2..6d641de0e7987 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/FastBootHibernateReactivePersistenceProvider.java @@ -84,9 +84,13 @@ public EntityManagerFactory createEntityManagerFactory(String emName, Map proper //if the provider is not set, don't use it as people might want to use Hibernate ORM if (IMPLEMENTATION_NAME.equalsIgnoreCase(unit.getProviderClassName()) || unit.getProviderClassName() == null) { - EntityManagerFactoryBuilder emfBuilder = getEntityManagerFactoryBuilderOrNull(emName, properties); - EntityManagerFactory emf = emfBuilder.build(); - return emf; + EntityManagerFactoryBuilder builder = getEntityManagerFactoryBuilderOrNull(emName, properties); + if (builder == null) { + log.trace("Could not obtain matching EntityManagerFactoryBuilder, returning null"); + return null; + } else { + return builder.build(); + } } } @@ -119,7 +123,7 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String Map puConfigMap = hibernateOrmRuntimeConfig .getAllPersistenceUnitConfigsAsMap(); - for (PersistenceUnitDescriptor persistenceUnit : units) { + for (RuntimePersistenceUnitDescriptor persistenceUnit : units) { log.debugf( "Checking persistence-unit [name=%s, explicit-provider=%s] against incoming persistence unit name [%s]", persistenceUnit.getName(), persistenceUnit.getProviderClassName(), persistenceUnitName); @@ -145,10 +149,15 @@ private EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String RuntimeSettings.Builder runtimeSettingsBuilder = new RuntimeSettings.Builder(buildTimeSettings, integrationSettings); + var puConfig = puConfigMap.getOrDefault(persistenceUnit.getConfigurationName(), + new HibernateOrmRuntimeConfigPersistenceUnit()); + if (puConfig.active.isPresent() && !puConfig.active.get()) { + throw new IllegalStateException( + "Attempting to boot a deactivated Hibernate Reactive persistence unit"); + } + // Inject runtime configuration if the persistence unit was defined by Quarkus configuration if (!recordedState.isFromPersistenceXml()) { - var puConfig = puConfigMap.getOrDefault(persistenceUnitName, - new HibernateOrmRuntimeConfigPersistenceUnit()); injectRuntimeConfiguration(puConfig, runtimeSettingsBuilder); } diff --git a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionFactoryProducer.java b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionFactoryProducer.java index 51d0564e0aa1b..e2a664cd5a8ec 100644 --- a/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionFactoryProducer.java +++ b/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/ReactiveSessionFactoryProducer.java @@ -13,6 +13,7 @@ import io.quarkus.arc.DefaultBean; import io.quarkus.arc.Unremovable; +import io.quarkus.hibernate.orm.runtime.JPAConfig; public class ReactiveSessionFactoryProducer { @@ -20,12 +21,22 @@ public class ReactiveSessionFactoryProducer { @PersistenceUnit EntityManagerFactory emf; + @Inject + JPAConfig jpaConfig; + @Produces @ApplicationScoped @DefaultBean @Unremovable @Typed({ Mutiny.SessionFactory.class, MutinyImplementor.class }) public MutinySessionFactoryImpl mutinySessionFactory() { + if (jpaConfig.getDeactivatedPersistenceUnitNames() + .contains(HibernateReactive.DEFAULT_REACTIVE_PERSISTENCE_UNIT_NAME)) { + throw new IllegalStateException( + "Cannot retrieve the Mutiny.SessionFactory for persistence unit " + + HibernateReactive.DEFAULT_REACTIVE_PERSISTENCE_UNIT_NAME + + ": Hibernate Reactive was deactivated through configuration properties"); + } // TODO Remove this cast when we get rid of the dependency to MutinyImplementor return (MutinySessionFactoryImpl) emf.unwrap(Mutiny.SessionFactory.class); } diff --git a/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleHibernateOrmActiveFalseAndNamedPuActiveTrueTest.java b/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleHibernateOrmActiveFalseAndNamedPuActiveTrueTest.java new file mode 100644 index 0000000000000..4770f58db5cef --- /dev/null +++ b/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleHibernateOrmActiveFalseAndNamedPuActiveTrueTest.java @@ -0,0 +1,56 @@ +package io.quarkus.test.devconsole; + +import org.hamcrest.Matchers; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; +import io.quarkus.test.devconsole.namedpu.MyNamedPuEntity; +import io.restassured.RestAssured; + +/** + * Note that this test cannot be placed under the relevant {@code -deployment} module because then the DEV UI processor would + * not be able to locate the template resources correctly. + */ +public class DevConsoleHibernateOrmActiveFalseAndNamedPuActiveTrueTest { + + @RegisterExtension + static final QuarkusDevModeTest test = new QuarkusDevModeTest() + .withApplicationRoot((jar) -> jar.addAsResource( + new StringAsset("quarkus.datasource.db-kind=h2\n" + + "quarkus.datasource.jdbc.url=jdbc:h2:mem:test\n" + + "quarkus.datasource.\"nameddatasource\".db-kind=h2\n" + + "quarkus.datasource.\"nameddatasource\".jdbc.url=jdbc:h2:mem:test2\n" + // Hibernate ORM is inactive for the default PU + + "quarkus.hibernate-orm.active=false\n" + + "quarkus.hibernate-orm.datasource=\n" + + "quarkus.hibernate-orm.packages=io.quarkus.test.devconsole\n" + // ... but it's (implicitly) active for a named PU + + "quarkus.hibernate-orm.\"namedpu\".datasource=nameddatasource\n" + + "quarkus.hibernate-orm.\"namedpu\".packages=io.quarkus.test.devconsole.namedpu\n"), + "application.properties") + .addClasses(MyEntity.class) + .addClasses(MyNamedPuEntity.class)); + + @Test + public void testPages() { + RestAssured.get("q/dev/io.quarkus.quarkus-hibernate-orm/persistence-units") + .then() + .statusCode(200) + .body(Matchers.not(Matchers.containsString("<default>"))) + .body(Matchers.containsString("Persistence Unit namedpu")); + + RestAssured.get("q/dev/io.quarkus.quarkus-hibernate-orm/managed-entities") + .then() + .statusCode(200) + .body(Matchers.not(Matchers.containsString(MyEntity.class.getName()))) + .body(Matchers.containsString(MyNamedPuEntity.class.getName())); + + RestAssured.get("q/dev/io.quarkus.quarkus-hibernate-orm/named-queries") + .then() + .statusCode(200) + .body(Matchers.containsString("No named queries were found.")); + } + +} diff --git a/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleHibernateOrmActiveFalseTest.java b/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleHibernateOrmActiveFalseTest.java new file mode 100644 index 0000000000000..6f7b13faeb4f7 --- /dev/null +++ b/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleHibernateOrmActiveFalseTest.java @@ -0,0 +1,45 @@ +package io.quarkus.test.devconsole; + +import org.hamcrest.Matchers; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; +import io.restassured.RestAssured; + +/** + * Note that this test cannot be placed under the relevant {@code -deployment} module because then the DEV UI processor would + * not be able to locate the template resources correctly. + */ +public class DevConsoleHibernateOrmActiveFalseTest { + + @RegisterExtension + static final QuarkusDevModeTest test = new QuarkusDevModeTest() + .withApplicationRoot((jar) -> jar.addAsResource( + new StringAsset("quarkus.datasource.db-kind=h2\n" + + "quarkus.datasource.jdbc.url=jdbc:h2:mem:test\n" + // Hibernate ORM is inactive: the dev console should be empty. + + "quarkus.hibernate-orm.active=false\n"), + "application.properties") + .addClasses(MyEntity.class)); + + @Test + public void testPages() { + RestAssured.get("q/dev/io.quarkus.quarkus-hibernate-orm/persistence-units") + .then() + .statusCode(200) + .body(Matchers.containsString("No persistence units found")); + + RestAssured.get("q/dev/io.quarkus.quarkus-hibernate-orm/managed-entities") + .then() + .statusCode(200) + .body(Matchers.containsString("No persistence units were found")); + + RestAssured.get("q/dev/io.quarkus.quarkus-hibernate-orm/named-queries") + .then() + .statusCode(200) + .body(Matchers.containsString("No persistence units were found")); + } + +} diff --git a/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/namedpu/MyNamedPuEntity.java b/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/namedpu/MyNamedPuEntity.java new file mode 100644 index 0000000000000..cb718cb0d24eb --- /dev/null +++ b/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/namedpu/MyNamedPuEntity.java @@ -0,0 +1,14 @@ +package io.quarkus.test.devconsole.namedpu; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class MyNamedPuEntity { + + @Id + Long id; + + String field; + +} From 74e75bd77ca1f1413b0af00e47aa72038802f5f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 2 Aug 2022 16:32:39 +0200 Subject: [PATCH 11/13] Add build-time configuration property quarkus.hibernate-envers.enabled --- ...HibernateEnversAlwaysEnabledProcessor.java | 23 ++++++++++ .../HibernateEnversDisabledProcessor.java | 31 +++++++++++++ .../deployment/HibernateEnversEnabled.java | 24 +++++++++++ .../deployment/HibernateEnversProcessor.java | 18 ++------ ...onfigEnabledFalseAndAuditedEntityTest.java | 43 +++++++++++++++++++ .../HibernateEnversBuildTimeConfig.java | 13 ++++++ .../envers/HibernateEnversRecorder.java | 28 ++++++++++-- 7 files changed, 162 insertions(+), 18 deletions(-) create mode 100644 extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversAlwaysEnabledProcessor.java create mode 100644 extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversDisabledProcessor.java create mode 100644 extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversEnabled.java create mode 100644 extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigEnabledFalseAndAuditedEntityTest.java diff --git a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversAlwaysEnabledProcessor.java b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversAlwaysEnabledProcessor.java new file mode 100644 index 0000000000000..f214982eca6e2 --- /dev/null +++ b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversAlwaysEnabledProcessor.java @@ -0,0 +1,23 @@ +package io.quarkus.hibernate.envers.deployment; + +import io.quarkus.deployment.Feature; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.logging.LogCleanupFilterBuildItem; + +// Executed even if the extension is disabled, see https://github.com/quarkusio/quarkus/pull/26966/ +public final class HibernateEnversAlwaysEnabledProcessor { + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(Feature.HIBERNATE_ENVERS); + } + + @BuildStep + void setupLogFilters(BuildProducer filters) { + filters.produce(new LogCleanupFilterBuildItem("org.hibernate.envers.boot.internal.EnversServiceImpl", + "Envers integration enabled")); + } + +} diff --git a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversDisabledProcessor.java b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversDisabledProcessor.java new file mode 100644 index 0000000000000..3370dc18367e0 --- /dev/null +++ b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversDisabledProcessor.java @@ -0,0 +1,31 @@ +package io.quarkus.hibernate.envers.deployment; + +import java.util.List; + +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.hibernate.envers.HibernateEnversRecorder; +import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem; +import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationStaticConfiguredBuildItem; + +@BuildSteps(onlyIfNot = HibernateEnversEnabled.class) +public final class HibernateEnversDisabledProcessor { + + @BuildStep + @Record(ExecutionTime.STATIC_INIT) + public void disableHibernateEnvers(HibernateEnversRecorder recorder, + List persistenceUnitDescriptorBuildItems, + BuildProducer integrationProducer) { + for (PersistenceUnitDescriptorBuildItem puDescriptor : persistenceUnitDescriptorBuildItems) { + integrationProducer.produce( + new HibernateOrmIntegrationStaticConfiguredBuildItem(HibernateEnversProcessor.HIBERNATE_ENVERS, + puDescriptor.getPersistenceUnitName()) + .setInitListener(recorder.createStaticInitInactiveListener()) + // We don't need XML mapping if Envers is disabled + .setXmlMappingRequired(false)); + } + } +} diff --git a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversEnabled.java b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversEnabled.java new file mode 100644 index 0000000000000..5451c0d2828e3 --- /dev/null +++ b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversEnabled.java @@ -0,0 +1,24 @@ +package io.quarkus.hibernate.envers.deployment; + +import java.util.function.BooleanSupplier; + +import io.quarkus.hibernate.envers.HibernateEnversBuildTimeConfig; + +/** + * Supplier that can be used to only run build steps + * if the Hibernate Envers extension is enabled. + */ +public class HibernateEnversEnabled implements BooleanSupplier { + + private final HibernateEnversBuildTimeConfig config; + + HibernateEnversEnabled(HibernateEnversBuildTimeConfig config) { + this.config = config; + } + + @Override + public boolean getAsBoolean() { + return config.enabled; + } + +} diff --git a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java index b28e13f115f92..26ef398a317e7 100644 --- a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java +++ b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java @@ -3,28 +3,22 @@ import java.util.Arrays; import java.util.List; -import io.quarkus.deployment.Feature; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; -import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; -import io.quarkus.deployment.logging.LogCleanupFilterBuildItem; import io.quarkus.hibernate.envers.HibernateEnversBuildTimeConfig; import io.quarkus.hibernate.envers.HibernateEnversRecorder; import io.quarkus.hibernate.orm.deployment.AdditionalJpaModelBuildItem; import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem; import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationStaticConfiguredBuildItem; +@BuildSteps(onlyIf = HibernateEnversEnabled.class) public final class HibernateEnversProcessor { - private static final String HIBERNATE_ENVERS = "Hibernate Envers"; - - @BuildStep - FeatureBuildItem feature() { - return new FeatureBuildItem(Feature.HIBERNATE_ENVERS); - } + static final String HIBERNATE_ENVERS = "Hibernate Envers"; @BuildStep List addJpaModelClasses() { @@ -61,10 +55,4 @@ public void applyConfig(HibernateEnversRecorder recorder, HibernateEnversBuildTi .setXmlMappingRequired(true)); } } - - @BuildStep - void setupLogFilters(BuildProducer filters) { - filters.produce(new LogCleanupFilterBuildItem("org.hibernate.envers.boot.internal.EnversServiceImpl", - "Envers integration enabled")); - } } diff --git a/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigEnabledFalseAndAuditedEntityTest.java b/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigEnabledFalseAndAuditedEntityTest.java new file mode 100644 index 0000000000000..1dd0e45d9cfc8 --- /dev/null +++ b/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigEnabledFalseAndAuditedEntityTest.java @@ -0,0 +1,43 @@ +package io.quarkus.hibernate.orm.envers.config; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import javax.inject.Inject; +import javax.persistence.metamodel.Bindable; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.envers.AuditReaderFactory; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.hibernate.orm.envers.MyAuditedEntity; +import io.quarkus.test.QuarkusUnitTest; + +public class ConfigEnabledFalseAndAuditedEntityTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar.addClass(MyAuditedEntity.class)) + .withConfigurationResource("application.properties") + .overrideConfigKey("quarkus.hibernate-envers.enabled", "false"); + + @Inject + SessionFactory sessionFactory; + + @Test + @SuppressWarnings("rawtypes") + public void test() { + assertThat(sessionFactory.getMetamodel().getEntities()) + .extracting(Bindable::getBindableJavaType) + // In particular this should not contain the revision entity + .containsExactly((Class) MyAuditedEntity.class); + + try (Session session = sessionFactory.openSession()) { + assertThatThrownBy(() -> AuditReaderFactory.get(session).isEntityClassAudited(MyAuditedEntity.class)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Service is not yet initialized"); + } + } +} diff --git a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfig.java b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfig.java index 1a7ddb61480b5..2fe0589541dab 100644 --- a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfig.java +++ b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfig.java @@ -8,6 +8,19 @@ @ConfigRoot(phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) public class HibernateEnversBuildTimeConfig { + /** + * Whether Hibernate Envers is enabled during the build. + * + * If Hibernate Envers is disabled during the build, all processing related to Hibernate Envers will be skipped, + * and the audit entities will not be added to the Hibernate ORM metamodel + * nor to the database schema that Hibernate ORM generates, + * but it will not be possible to use Hibernate Envers at runtime. + * + * @asciidoclet + */ + @ConfigItem(defaultValue = "true") + public boolean enabled; + /** * Enable store_data_at_delete feature. * Maps to {@link org.hibernate.envers.configuration.EnversSettings#STORE_DATA_AT_DELETE}. diff --git a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversRecorder.java b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversRecorder.java index a1b6a3426d25f..c895a273b6692 100644 --- a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversRecorder.java +++ b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversRecorder.java @@ -5,6 +5,7 @@ import org.hibernate.boot.Metadata; import org.hibernate.boot.spi.BootstrapContext; +import org.hibernate.envers.boot.internal.EnversService; import org.hibernate.envers.configuration.EnversSettings; import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticInitListener; @@ -14,13 +15,14 @@ public class HibernateEnversRecorder { public HibernateOrmIntegrationStaticInitListener createStaticInitListener(HibernateEnversBuildTimeConfig buildTimeConfig) { - return new HibernateEnversIntegrationListener(buildTimeConfig); + return new HibernateEnversIntegrationStaticInitListener(buildTimeConfig); } - private static final class HibernateEnversIntegrationListener implements HibernateOrmIntegrationStaticInitListener { + private static final class HibernateEnversIntegrationStaticInitListener + implements HibernateOrmIntegrationStaticInitListener { private HibernateEnversBuildTimeConfig buildTimeConfig; - private HibernateEnversIntegrationListener(HibernateEnversBuildTimeConfig buildTimeConfig) { + private HibernateEnversIntegrationStaticInitListener(HibernateEnversBuildTimeConfig buildTimeConfig) { this.buildTimeConfig = buildTimeConfig; } @@ -81,4 +83,24 @@ public void onMetadataInitialized(Metadata metadata, BootstrapContext bootstrapC BiConsumer propertyCollector) { } } + + public HibernateOrmIntegrationStaticInitListener createStaticInitInactiveListener() { + return new HibernateEnversIntegrationStaticInitInactiveListener(); + } + + private static final class HibernateEnversIntegrationStaticInitInactiveListener + implements HibernateOrmIntegrationStaticInitListener { + private HibernateEnversIntegrationStaticInitInactiveListener() { + } + + @Override + public void contributeBootProperties(BiConsumer propertyCollector) { + propertyCollector.accept(EnversService.INTEGRATION_ENABLED, "false"); + } + + @Override + public void onMetadataInitialized(Metadata metadata, BootstrapContext bootstrapContext, + BiConsumer propertyCollector) { + } + } } From 9bf94fcb43f08c7d7a06f05fed3a559451e2e865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 3 Aug 2022 14:11:36 +0200 Subject: [PATCH 12/13] Add build-time configuration property quarkus.hibernate-envers.active --- .../HibernateEnversDisabledProcessor.java | 31 +++++++++++- .../deployment/HibernateEnversProcessor.java | 8 +-- ...ConfigActiveFalseAndAuditedEntityTest.java | 42 ++++++++++++++++ .../ConfigEnabledFalseAndActiveTrueTest.java | 50 +++++++++++++++++++ ...onfigEnabledFalseAndAuditedEntityTest.java | 2 +- .../HibernateEnversBuildTimeConfig.java | 44 +++++++++++++++- ...eEnversBuildTimeConfigPersistenceUnit.java | 30 +++++++++++ .../envers/HibernateEnversRecorder.java | 18 +++++-- 8 files changed, 214 insertions(+), 11 deletions(-) create mode 100644 extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigActiveFalseAndAuditedEntityTest.java create mode 100644 extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigEnabledFalseAndActiveTrueTest.java create mode 100644 extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfigPersistenceUnit.java diff --git a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversDisabledProcessor.java b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversDisabledProcessor.java index 3370dc18367e0..b2894c347c577 100644 --- a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversDisabledProcessor.java +++ b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversDisabledProcessor.java @@ -1,24 +1,29 @@ package io.quarkus.hibernate.envers.deployment; import java.util.List; +import java.util.Set; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; +import io.quarkus.hibernate.envers.HibernateEnversBuildTimeConfig; import io.quarkus.hibernate.envers.HibernateEnversRecorder; import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem; import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationStaticConfiguredBuildItem; +import io.quarkus.runtime.configuration.ConfigurationException; @BuildSteps(onlyIfNot = HibernateEnversEnabled.class) public final class HibernateEnversDisabledProcessor { @BuildStep @Record(ExecutionTime.STATIC_INIT) - public void disableHibernateEnvers(HibernateEnversRecorder recorder, + public void disableHibernateEnversStaticInit(HibernateEnversRecorder recorder, + HibernateEnversBuildTimeConfig buildTimeConfig, List persistenceUnitDescriptorBuildItems, BuildProducer integrationProducer) { + checkNoExplicitActiveTrue(buildTimeConfig); for (PersistenceUnitDescriptorBuildItem puDescriptor : persistenceUnitDescriptorBuildItems) { integrationProducer.produce( new HibernateOrmIntegrationStaticConfiguredBuildItem(HibernateEnversProcessor.HIBERNATE_ENVERS, @@ -28,4 +33,28 @@ public void disableHibernateEnvers(HibernateEnversRecorder recorder, .setXmlMappingRequired(false)); } } + + // TODO move this to runtime init once we implement in Hibernate ORM a way + // to remove entity types from the metamodel on runtime init + public void checkNoExplicitActiveTrue(HibernateEnversBuildTimeConfig buildTimeConfig) { + for (var entry : buildTimeConfig.getAllPersistenceUnitConfigsAsMap().entrySet()) { + var config = entry.getValue(); + if (config.active.isPresent() && config.active.get()) { + var puName = entry.getKey(); + String enabledPropertyKey = HibernateEnversBuildTimeConfig.extensionPropertyKey("enabled"); + String activePropertyKey = HibernateEnversBuildTimeConfig.persistenceUnitPropertyKey(puName, "active"); + throw new ConfigurationException( + "Hibernate Envers activated explicitly for persistence unit '" + puName + + "', but the Hibernate Envers extension was disabled at build time." + + " If you want Hibernate Envers to be active for this persistence unit, you must set '" + + enabledPropertyKey + + "' to 'true' at build time." + + " If you don't want Hibernate Envers to be active for this persistence unit, you must leave '" + + activePropertyKey + + "' unset or set it to 'false'.", + Set.of(enabledPropertyKey, activePropertyKey)); + } + } + } + } diff --git a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java index 26ef398a317e7..ad2ff3d70b9f9 100644 --- a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java +++ b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/HibernateEnversProcessor.java @@ -44,14 +44,14 @@ public void registerEnversReflections(BuildProducer re @BuildStep @Record(ExecutionTime.STATIC_INIT) - public void applyConfig(HibernateEnversRecorder recorder, HibernateEnversBuildTimeConfig buildTimeConfig, + public void applyStaticConfig(HibernateEnversRecorder recorder, HibernateEnversBuildTimeConfig buildTimeConfig, List persistenceUnitDescriptorBuildItems, BuildProducer integrationProducer) { for (PersistenceUnitDescriptorBuildItem puDescriptor : persistenceUnitDescriptorBuildItems) { + String puName = puDescriptor.getPersistenceUnitName(); integrationProducer.produce( - new HibernateOrmIntegrationStaticConfiguredBuildItem(HIBERNATE_ENVERS, - puDescriptor.getPersistenceUnitName()) - .setInitListener(recorder.createStaticInitListener(buildTimeConfig)) + new HibernateOrmIntegrationStaticConfiguredBuildItem(HIBERNATE_ENVERS, puName) + .setInitListener(recorder.createStaticInitListener(buildTimeConfig, puName)) .setXmlMappingRequired(true)); } } diff --git a/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigActiveFalseAndAuditedEntityTest.java b/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigActiveFalseAndAuditedEntityTest.java new file mode 100644 index 0000000000000..aa43bd037f1af --- /dev/null +++ b/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigActiveFalseAndAuditedEntityTest.java @@ -0,0 +1,42 @@ +package io.quarkus.hibernate.orm.envers.config; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import javax.inject.Inject; +import javax.persistence.metamodel.Bindable; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.envers.AuditReaderFactory; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.hibernate.orm.envers.MyAuditedEntity; +import io.quarkus.test.QuarkusUnitTest; + +public class ConfigActiveFalseAndAuditedEntityTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar.addClass(MyAuditedEntity.class)) + .withConfigurationResource("application.properties") + .overrideConfigKey("quarkus.hibernate-envers.active", "false"); + + @Inject + SessionFactory sessionFactory; + + @Test + public void test() { + assertThat(sessionFactory.getMetamodel().getEntities()) + .extracting(Bindable::getBindableJavaType) + // In particular this should not contain the revision entity + .containsExactlyInAnyOrder((Class) MyAuditedEntity.class); + + try (Session session = sessionFactory.openSession()) { + assertThatThrownBy(() -> AuditReaderFactory.get(session).isEntityClassAudited(MyAuditedEntity.class)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Service is not yet initialized"); + } + } +} diff --git a/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigEnabledFalseAndActiveTrueTest.java b/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigEnabledFalseAndActiveTrueTest.java new file mode 100644 index 0000000000000..79d05890ed219 --- /dev/null +++ b/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigEnabledFalseAndActiveTrueTest.java @@ -0,0 +1,50 @@ +package io.quarkus.hibernate.orm.envers.config; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import javax.inject.Inject; +import javax.persistence.metamodel.Bindable; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.envers.AuditReaderFactory; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.hibernate.orm.envers.MyAuditedEntity; +import io.quarkus.runtime.configuration.ConfigurationException; +import io.quarkus.test.QuarkusUnitTest; + +public class ConfigEnabledFalseAndActiveTrueTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar.addClass(MyAuditedEntity.class)) + .withConfigurationResource("application.properties") + .overrideConfigKey("quarkus.hibernate-envers.enabled", "false") + .overrideConfigKey("quarkus.hibernate-envers.active", "true") + .assertException(throwable -> assertThat(throwable) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining( + "Hibernate Envers activated explicitly for persistence unit '', but the Hibernate Envers extension was disabled at build time", + "If you want Hibernate Envers to be active for this persistence unit, you must set 'quarkus.hibernate-envers.enabled' to 'true' at build time", + "If you don't want Hibernate Envers to be active for this persistence unit, you must leave 'quarkus.hibernate-envers.active' unset or set it to 'false'")); + + @Inject + SessionFactory sessionFactory; + + @Test + public void test() { + assertThat(sessionFactory.getMetamodel().getEntities()) + .extracting(Bindable::getBindableJavaType) + // In particular this should not contain the revision entity + .containsExactlyInAnyOrder((Class) MyAuditedEntity.class); + + try (Session session = sessionFactory.openSession()) { + assertThatThrownBy(() -> AuditReaderFactory.get(session).isEntityClassAudited(MyAuditedEntity.class)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Service is not yet initialized"); + } + } +} diff --git a/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigEnabledFalseAndAuditedEntityTest.java b/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigEnabledFalseAndAuditedEntityTest.java index 1dd0e45d9cfc8..d6635ea0feb28 100644 --- a/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigEnabledFalseAndAuditedEntityTest.java +++ b/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/config/ConfigEnabledFalseAndAuditedEntityTest.java @@ -32,7 +32,7 @@ public void test() { assertThat(sessionFactory.getMetamodel().getEntities()) .extracting(Bindable::getBindableJavaType) // In particular this should not contain the revision entity - .containsExactly((Class) MyAuditedEntity.class); + .containsExactlyInAnyOrder((Class) MyAuditedEntity.class); try (Session session = sessionFactory.openSession()) { assertThatThrownBy(() -> AuditReaderFactory.get(session).isEntityClassAudited(MyAuditedEntity.class)) diff --git a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfig.java b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfig.java index 2fe0589541dab..bfe1b16a289ac 100644 --- a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfig.java +++ b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfig.java @@ -1,7 +1,12 @@ package io.quarkus.hibernate.envers; +import java.util.Map; import java.util.Optional; +import java.util.TreeMap; +import io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil; +import io.quarkus.runtime.annotations.ConfigDocMapKey; +import io.quarkus.runtime.annotations.ConfigDocSection; import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; @@ -14,13 +19,37 @@ public class HibernateEnversBuildTimeConfig { * If Hibernate Envers is disabled during the build, all processing related to Hibernate Envers will be skipped, * and the audit entities will not be added to the Hibernate ORM metamodel * nor to the database schema that Hibernate ORM generates, - * but it will not be possible to use Hibernate Envers at runtime. + * but it will not be possible to use Hibernate Envers at runtime: + * `quarkus.hibernate-envers.active` will default to `false` and setting it to `true` will lead to an error. * * @asciidoclet */ @ConfigItem(defaultValue = "true") public boolean enabled; + /** + * Configuration for the default persistence unit. + */ + @ConfigItem(name = ConfigItem.PARENT) + public HibernateEnversBuildTimeConfigPersistenceUnit defaultPersistenceUnit; + + /** + * Configuration for additional named persistence units. + */ + @ConfigDocSection + @ConfigDocMapKey("persistence-unit-name") + @ConfigItem(name = ConfigItem.PARENT) + public Map persistenceUnits; + + public Map getAllPersistenceUnitConfigsAsMap() { + Map map = new TreeMap<>(); + if (defaultPersistenceUnit != null) { + map.put(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, defaultPersistenceUnit); + } + map.putAll(persistenceUnits); + return map; + } + /** * Enable store_data_at_delete feature. * Maps to {@link org.hibernate.envers.configuration.EnversSettings#STORE_DATA_AT_DELETE}. @@ -177,4 +206,17 @@ public class HibernateEnversBuildTimeConfig { */ @ConfigItem(defaultValue = "org.hibernate.envers.boot.internal.LegacyModifiedColumnNamingStrategy") public Optional modifiedColumnNamingStrategy; + + public static String extensionPropertyKey(String radical) { + return "quarkus.hibernate-envers." + radical; + } + + public static String persistenceUnitPropertyKey(String persistenceUnitName, String radical) { + StringBuilder keyBuilder = new StringBuilder("quarkus.hibernate-envers."); + if (!PersistenceUnitUtil.isDefaultPersistenceUnit(persistenceUnitName)) { + keyBuilder.append("\"").append(persistenceUnitName).append("\"."); + } + keyBuilder.append(radical); + return keyBuilder.toString(); + } } diff --git a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfigPersistenceUnit.java b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfigPersistenceUnit.java new file mode 100644 index 0000000000000..c71c07716308e --- /dev/null +++ b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfigPersistenceUnit.java @@ -0,0 +1,30 @@ +package io.quarkus.hibernate.envers; + +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +@ConfigGroup +public class HibernateEnversBuildTimeConfigPersistenceUnit { + + /** + * Whether Hibernate Envers should be active for this persistence unit at runtime. + * + * If Hibernate Envers is not active, the audit entities will *still* be added to the Hibernate ORM metamodel + * and to the database schema that Hibernate ORM generates: + * you would need to disable Hibernate Envers at build time (i.e. set `quarkus.hibernate-envers.enabled` to `false`) + * in order to avoid that. + * However, when Hibernate Envers is not active, it will not process entity change events + * nor create new versions of entities. + * and accessing the AuditReader through AuditReaderFactory will not be possible. + * + * Note that if Hibernate Envers is disabled (i.e. `quarkus.hibernate-envers.enabled` is set to `false`), + * it won't be active for any persistence unit, and setting this property to `true` will fail. + * + * @asciidoclet + */ + @ConfigItem(defaultValueDocumentation = "`true` if Hibernate ORM is enabled; `false` otherwise") + public Optional active = Optional.empty(); + +} diff --git a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversRecorder.java b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversRecorder.java index c895a273b6692..b6df7ccd5d8d1 100644 --- a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversRecorder.java +++ b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversRecorder.java @@ -14,20 +14,30 @@ @Recorder public class HibernateEnversRecorder { - public HibernateOrmIntegrationStaticInitListener createStaticInitListener(HibernateEnversBuildTimeConfig buildTimeConfig) { - return new HibernateEnversIntegrationStaticInitListener(buildTimeConfig); + public HibernateOrmIntegrationStaticInitListener createStaticInitListener(HibernateEnversBuildTimeConfig buildTimeConfig, + String puName) { + return new HibernateEnversIntegrationStaticInitListener(buildTimeConfig, puName); } private static final class HibernateEnversIntegrationStaticInitListener implements HibernateOrmIntegrationStaticInitListener { - private HibernateEnversBuildTimeConfig buildTimeConfig; + private final HibernateEnversBuildTimeConfig buildTimeConfig; + private final String puName; - private HibernateEnversIntegrationStaticInitListener(HibernateEnversBuildTimeConfig buildTimeConfig) { + private HibernateEnversIntegrationStaticInitListener(HibernateEnversBuildTimeConfig buildTimeConfig, String puName) { this.buildTimeConfig = buildTimeConfig; + this.puName = puName; } @Override public void contributeBootProperties(BiConsumer propertyCollector) { + var puConfig = buildTimeConfig.getAllPersistenceUnitConfigsAsMap().get(puName); + if (puConfig != null && puConfig.active.isPresent() && !puConfig.active.get()) { + propertyCollector.accept(EnversService.INTEGRATION_ENABLED, "false"); + // Do not process other properties: Hibernate Envers is inactive anyway. + return; + } + addConfig(propertyCollector, EnversSettings.STORE_DATA_AT_DELETE, buildTimeConfig.storeDataAtDelete); addConfig(propertyCollector, EnversSettings.AUDIT_TABLE_SUFFIX, buildTimeConfig.auditTableSuffix); addConfig(propertyCollector, EnversSettings.AUDIT_TABLE_PREFIX, buildTimeConfig.auditTablePrefix); From 38eb92a82a6b495bbe169bc303ae80d7dc09c6b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 3 Aug 2022 16:32:31 +0200 Subject: [PATCH 13/13] Align Hibernate Search's error messages and documentation for .enable/.active on ORM's --- .../ConfigEnabledFalseAndActiveTrueTest.java | 6 +++--- .../runtime/HibernateSearchElasticsearchRecorder.java | 8 +++++--- ...teSearchElasticsearchRuntimeConfigPersistenceUnit.java | 7 ++++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/orm/elasticsearch/test/configuration/ConfigEnabledFalseAndActiveTrueTest.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/orm/elasticsearch/test/configuration/ConfigEnabledFalseAndActiveTrueTest.java index 00722a3303d39..27d173c0beefa 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/orm/elasticsearch/test/configuration/ConfigEnabledFalseAndActiveTrueTest.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/test/java/io/quarkus/hibernate/search/orm/elasticsearch/test/configuration/ConfigEnabledFalseAndActiveTrueTest.java @@ -21,9 +21,9 @@ public class ConfigEnabledFalseAndActiveTrueTest { .assertException(throwable -> assertThat(throwable) .isInstanceOf(ConfigurationException.class) .hasMessageContainingAll( - "Hibernate Search activated explicitly, but Hibernate Search was disabled at build time", - "If you want Hibernate Search to be active at runtime, you must set 'quarkus.hibernate-search-orm.enabled' to 'true' at build time", - "If you don't want Hibernate Search to be active at runtime, you must leave 'quarkus.hibernate-search-orm.active' unset or set it to 'false'")); + "Hibernate Search activated explicitly for persistence unit '', but the Hibernate Search extension was disabled at build time", + "If you want Hibernate Search to be active for this persistence unit, you must set 'quarkus.hibernate-search-orm.enabled' to 'true' at build time", + "If you don't want Hibernate Search to be active for this persistence unit, you must leave 'quarkus.hibernate-search-orm.active' unset or set it to 'false'")); @Test public void test() { diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java index 1f692cd772cd9..80619b2a2bcba 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java @@ -83,10 +83,12 @@ public void checkNoExplicitActiveTrue(HibernateSearchElasticsearchRuntimeConfig String enabledPropertyKey = HibernateSearchElasticsearchRuntimeConfig.extensionPropertyKey("enabled"); String activePropertyKey = HibernateSearchElasticsearchRuntimeConfig.mapperPropertyKey(puName, "active"); throw new ConfigurationException( - "Hibernate Search activated explicitly, but Hibernate Search was disabled at build time." - + " If you want Hibernate Search to be active at runtime, you must set '" + enabledPropertyKey + "Hibernate Search activated explicitly for persistence unit '" + puName + + "', but the Hibernate Search extension was disabled at build time." + + " If you want Hibernate Search to be active for this persistence unit, you must set '" + + enabledPropertyKey + "' to 'true' at build time." - + " If you don't want Hibernate Search to be active at runtime, you must leave '" + + " If you don't want Hibernate Search to be active for this persistence unit, you must leave '" + activePropertyKey + "' unset or set it to 'false'.", Set.of(enabledPropertyKey, activePropertyKey)); diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java index 2c0679be92d17..6979394ca30cc 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java @@ -23,13 +23,14 @@ public class HibernateSearchElasticsearchRuntimeConfigPersistenceUnit { /** - * Whether Hibernate Search should be active at runtime. + * Whether Hibernate Search should be active for this persistence unit at runtime. * * If Hibernate Search is not active, it won't index Hibernate ORM entities, - * and accessing the SearchMapping/SearchSession for search or other operation will not be possible. + * and accessing the SearchMapping/SearchSession of the relevant persistence unit + * for search or other operation will not be possible. * * Note that if Hibernate Search is disabled (i.e. `quarkus.hibernate-search-orm.enabled` is set to `false`), - * it won't be active, and setting this property to `true` will fail. + * it won't be active for any persistence unit, and setting this property to `true` will fail. * * @asciidoclet */