From 64980d2f66d297f52a921eb82e69c687587e04a5 Mon Sep 17 00:00:00 2001 From: Juri Berlanda Date: Sat, 15 Aug 2020 16:56:03 +0200 Subject: [PATCH] [Envers] Implement support for store_data_at_delete. Signed-off-by: Juri Berlanda --- .../QuarkusHibernateEnversProcessor.java | 10 +++ .../EnversStoreDataAtDeleteTestCase.java | 28 ++++++++ .../EnversTestStoreDataAtDeleteResource.java | 64 +++++++++++++++++++ ...ation-with-store-data-at-delete.properties | 5 ++ .../HibernateEnversBuildTimeConfig.java | 14 ++++ .../envers/HibernateEnversRecorder.java | 46 +++++++++++++ 6 files changed, 167 insertions(+) create mode 100644 extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/EnversStoreDataAtDeleteTestCase.java create mode 100644 extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/EnversTestStoreDataAtDeleteResource.java create mode 100644 extensions/hibernate-envers/deployment/src/test/resources/application-with-store-data-at-delete.properties create mode 100644 extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfig.java create mode 100644 extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversRecorder.java diff --git a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/QuarkusHibernateEnversProcessor.java b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/QuarkusHibernateEnversProcessor.java index 458f0bf28a9bb..6b69b7928c5b8 100644 --- a/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/QuarkusHibernateEnversProcessor.java +++ b/extensions/hibernate-envers/deployment/src/main/java/io/quarkus/hibernate/envers/deployment/QuarkusHibernateEnversProcessor.java @@ -3,8 +3,12 @@ import io.quarkus.deployment.Feature; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +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.hibernate.envers.HibernateEnversBuildTimeConfig; +import io.quarkus.hibernate.envers.HibernateEnversRecorder; public final class QuarkusHibernateEnversProcessor { @@ -22,4 +26,10 @@ public void registerEnversReflections(BuildProducer re new ReflectiveClassBuildItem(false, false, "org.hibernate.tuple.component.DynamicMapComponentTuplizer")); } + @BuildStep + @Record(ExecutionTime.STATIC_INIT) + public void applyConfig(HibernateEnversRecorder recorder, + HibernateEnversBuildTimeConfig buildTimeConfig) { + recorder.registerHibernateEnversIntegration(buildTimeConfig); + } } diff --git a/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/EnversStoreDataAtDeleteTestCase.java b/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/EnversStoreDataAtDeleteTestCase.java new file mode 100644 index 0000000000000..b578d321972a5 --- /dev/null +++ b/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/EnversStoreDataAtDeleteTestCase.java @@ -0,0 +1,28 @@ +package io.quarkus.hibernate.orm.envers; + +import static org.hamcrest.Matchers.is; + +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.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class EnversStoreDataAtDeleteTestCase { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(MyAuditedEntity.class, MyRevisionEntity.class, MyRevisionListener.class, + EnversTestStoreDataAtDeleteResource.class) + .addAsResource("application-with-store-data-at-delete.properties", "application.properties")); + + @Test + public void testStoreDataAtDelete() { + RestAssured.when().delete("/envers-store-data-at-delete").then() + .body(is("OK")); + } + +} diff --git a/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/EnversTestStoreDataAtDeleteResource.java b/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/EnversTestStoreDataAtDeleteResource.java new file mode 100644 index 0000000000000..fead8b7164435 --- /dev/null +++ b/extensions/hibernate-envers/deployment/src/test/java/io/quarkus/hibernate/orm/envers/EnversTestStoreDataAtDeleteResource.java @@ -0,0 +1,64 @@ +package io.quarkus.hibernate.orm.envers; + +import java.util.List; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.persistence.EntityManager; +import javax.resource.spi.IllegalStateException; +import javax.transaction.UserTransaction; +import javax.ws.rs.DELETE; +import javax.ws.rs.Path; + +import org.hibernate.envers.AuditReader; +import org.hibernate.envers.AuditReaderFactory; + +@Path("/envers-store-data-at-delete") +@ApplicationScoped +public class EnversTestStoreDataAtDeleteResource { + private static final String NAME = "deleted"; + + @Inject + EntityManager em; + + @Inject + UserTransaction transaction; + + @DELETE + public String delete() { + try { + transaction.begin(); + MyAuditedEntity entity = new MyAuditedEntity(); + entity.setName(NAME); + em.persist(entity); + transaction.commit(); + + transaction.begin(); + em.remove(em.find(MyAuditedEntity.class, entity.getId())); + em.flush(); + transaction.commit(); + + AuditReader auditReader = AuditReaderFactory.get(em); + List revisions = auditReader.getRevisions(MyAuditedEntity.class, entity.getId()); + if (revisions.size() != 2) { + throw new IllegalStateException(String.format("found {} revisions", revisions.size())); + } + + for (Number revision : revisions) { + System.out.println(revision); + MyAuditedEntity revEntity = auditReader.find(MyAuditedEntity.class, MyAuditedEntity.class.getName(), + entity.getId(), revision, true); + if (revEntity == null) { + throw new IllegalStateException("failed to find delete revision"); + } + if (!NAME.equals(revEntity.getName())) { + throw new IllegalStateException("revision listener failed to persist data on delete"); + } + } + + return "OK"; + } catch (Exception exception) { + return exception.getMessage(); + } + } +} diff --git a/extensions/hibernate-envers/deployment/src/test/resources/application-with-store-data-at-delete.properties b/extensions/hibernate-envers/deployment/src/test/resources/application-with-store-data-at-delete.properties new file mode 100644 index 0000000000000..721ddda002278 --- /dev/null +++ b/extensions/hibernate-envers/deployment/src/test/resources/application-with-store-data-at-delete.properties @@ -0,0 +1,5 @@ +quarkus.datasource.db-kind=h2 +quarkus.datasource.jdbc.url=jdbc:h2:mem:test + +quarkus.hibernate-orm.database.generation=drop-and-create +quarkus.hibernate-envers.store-data-at-delete=true 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 new file mode 100644 index 0000000000000..da15823cfd519 --- /dev/null +++ b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfig.java @@ -0,0 +1,14 @@ +package io.quarkus.hibernate.envers; + +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 { + /** + * Enable store_data_at_delete feature. + */ + @ConfigItem(defaultValue = "false") + public boolean storeDataAtDelete; +} 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 new file mode 100644 index 0000000000000..e08d2aa60f937 --- /dev/null +++ b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversRecorder.java @@ -0,0 +1,46 @@ +package io.quarkus.hibernate.envers; + +import java.util.function.BiConsumer; + +import org.hibernate.boot.Metadata; +import org.hibernate.boot.spi.BootstrapContext; +import org.hibernate.envers.configuration.EnversSettings; + +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationListener; +import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrations; +import io.quarkus.runtime.annotations.Recorder; + +@Recorder +public class HibernateEnversRecorder { + + public void registerHibernateEnversIntegration(HibernateEnversBuildTimeConfig buildTimeConfig) { + HibernateOrmIntegrations.registerListener(new HibernateEnversIntegrationListener(buildTimeConfig)); + } + + private static final class HibernateEnversIntegrationListener implements HibernateOrmIntegrationListener { + + private HibernateEnversBuildTimeConfig buildTimeConfig; + + private HibernateEnversIntegrationListener(HibernateEnversBuildTimeConfig buildTimeConfig) { + this.buildTimeConfig = buildTimeConfig; + } + + @Override + public void contributeBootProperties(BiConsumer propertyCollector) { + addConfig(propertyCollector, EnversSettings.STORE_DATA_AT_DELETE, buildTimeConfig.storeDataAtDelete); + } + + public static void addConfig(BiConsumer propertyCollector, String configPath, T value) { + propertyCollector.accept(configPath, value); + } + + @Override + public void onMetadataInitialized(Metadata metadata, BootstrapContext bootstrapContext, + BiConsumer propertyCollector) { + } + + @Override + public void contributeRuntimeProperties(BiConsumer propertyCollector) { + } + } +}