Skip to content

Commit

Permalink
Merge pull request #27134 from yrodiere/orm-enabled-active
Browse files Browse the repository at this point in the history
Add .enabled/.active configuration properties to Hibernate ORM, Reactive and Envers extensions
  • Loading branch information
yrodiere authored Aug 11, 2022
2 parents 2fe0ba5 + 38eb92a commit 9a772a8
Show file tree
Hide file tree
Showing 55 changed files with 1,317 additions and 370 deletions.
Original file line number Diff line number Diff line change
@@ -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<LogCleanupFilterBuildItem> filters) {
filters.produce(new LogCleanupFilterBuildItem("org.hibernate.envers.boot.internal.EnversServiceImpl",
"Envers integration enabled"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
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 disableHibernateEnversStaticInit(HibernateEnversRecorder recorder,
HibernateEnversBuildTimeConfig buildTimeConfig,
List<PersistenceUnitDescriptorBuildItem> persistenceUnitDescriptorBuildItems,
BuildProducer<HibernateOrmIntegrationStaticConfiguredBuildItem> integrationProducer) {
checkNoExplicitActiveTrue(buildTimeConfig);
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));
}
}

// 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));
}
}
}

}
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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<AdditionalJpaModelBuildItem> addJpaModelClasses() {
Expand All @@ -50,21 +44,15 @@ public void registerEnversReflections(BuildProducer<ReflectiveClassBuildItem> re

@BuildStep
@Record(ExecutionTime.STATIC_INIT)
public void applyConfig(HibernateEnversRecorder recorder, HibernateEnversBuildTimeConfig buildTimeConfig,
public void applyStaticConfig(HibernateEnversRecorder recorder, HibernateEnversBuildTimeConfig buildTimeConfig,
List<PersistenceUnitDescriptorBuildItem> persistenceUnitDescriptorBuildItems,
BuildProducer<HibernateOrmIntegrationStaticConfiguredBuildItem> 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));
}
}

@BuildStep
void setupLogFilters(BuildProducer<LogCleanupFilterBuildItem> filters) {
filters.produce(new LogCleanupFilterBuildItem("org.hibernate.envers.boot.internal.EnversServiceImpl",
"Envers integration enabled"));
}
}
Original file line number Diff line number Diff line change
@@ -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");
}
}
}
Original file line number Diff line number Diff line change
@@ -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 '<default>', 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");
}
}
}
Original file line number Diff line number Diff line change
@@ -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
.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");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,55 @@
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;

@ConfigRoot(phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED)
public class HibernateEnversBuildTimeConfig {
/**
* Whether Hibernate Envers is enabled <strong>during the build</strong>.
*
* 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:
* `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<String, HibernateEnversBuildTimeConfigPersistenceUnit> persistenceUnits;

public Map<String, HibernateEnversBuildTimeConfigPersistenceUnit> getAllPersistenceUnitConfigsAsMap() {
Map<String, HibernateEnversBuildTimeConfigPersistenceUnit> 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}.
Expand Down Expand Up @@ -164,4 +206,17 @@ public class HibernateEnversBuildTimeConfig {
*/
@ConfigItem(defaultValue = "org.hibernate.envers.boot.internal.LegacyModifiedColumnNamingStrategy")
public Optional<String> 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();
}
}
Loading

0 comments on commit 9a772a8

Please sign in to comment.