From 0958313ebc0b4ebce2549b956c40cdc01173ced0 Mon Sep 17 00:00:00 2001 From: Lukasz Antoniak Date: Fri, 8 Feb 2013 11:15:43 +0100 Subject: [PATCH] HHH-7478 - Fix and test --- .../org/hibernate/engine/spi/ActionQueue.java | 11 ++- .../envers/tools/MutableInteger.java | 19 ++++- .../basic/RegisterUserEventListenersTest.java | 83 +++++++++++++++++++ 3 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/basic/RegisterUserEventListenersTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java index f4c943c10c..a70c2bf3d5 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java @@ -32,7 +32,9 @@ import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Queue; import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; import org.jboss.logging.Logger; @@ -643,7 +645,9 @@ public class ActionQueue { private static class BeforeTransactionCompletionProcessQueue { private SessionImplementor session; - private List processes = new ArrayList(); + // Concurrency handling required when transaction completion process is dynamically registered + // inside event listener (HHH-7478). + private Queue processes = new ConcurrentLinkedQueue(); private BeforeTransactionCompletionProcessQueue(SessionImplementor session) { this.session = session; @@ -675,8 +679,9 @@ public class ActionQueue { private static class AfterTransactionCompletionProcessQueue { private SessionImplementor session; private Set querySpacesToInvalidate = new HashSet(); - private List processes - = new ArrayList( INIT_QUEUE_LIST_SIZE * 3 ); + // Concurrency handling required when transaction completion process is dynamically registered + // inside event listener (HHH-7478). + private Queue processes = new ConcurrentLinkedQueue(); private AfterTransactionCompletionProcessQueue(SessionImplementor session) { this.session = session; diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/tools/MutableInteger.java b/hibernate-envers/src/main/java/org/hibernate/envers/tools/MutableInteger.java index cbfc2ccd78..a158eb45e1 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/tools/MutableInteger.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/tools/MutableInteger.java @@ -23,17 +23,32 @@ */ package org.hibernate.envers.tools; - /** * @author Adam Warski (adam at warski dot org) */ public class MutableInteger { - private int value; + private int value = 0; public MutableInteger() { } + public MutableInteger(int value) { + this.value = value; + } + public int getAndIncrease() { return value++; } + + public int get() { + return value; + } + + public void set(int value) { + this.value = value; + } + + public void increase() { + ++value; + } } \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/basic/RegisterUserEventListenersTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/basic/RegisterUserEventListenersTest.java new file mode 100644 index 0000000000..c7712e2de3 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/basic/RegisterUserEventListenersTest.java @@ -0,0 +1,83 @@ +package org.hibernate.envers.test.integration.basic; + +import org.junit.Assert; +import org.junit.Test; + +import org.hibernate.Session; +import org.hibernate.action.spi.AfterTransactionCompletionProcess; +import org.hibernate.action.spi.BeforeTransactionCompletionProcess; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.envers.test.BaseEnversFunctionalTestCase; +import org.hibernate.envers.test.entities.StrTestEntity; +import org.hibernate.envers.tools.MutableInteger; +import org.hibernate.event.service.spi.EventListenerRegistry; +import org.hibernate.event.spi.EventType; +import org.hibernate.event.spi.PostInsertEvent; +import org.hibernate.event.spi.PostInsertEventListener; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.testing.TestForIssue; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +public class RegisterUserEventListenersTest extends BaseEnversFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { StrTestEntity.class }; + } + + @Test + @TestForIssue( jiraKey = "HHH-7478" ) + public void testTransactionProcessSynchronization() { + final EventListenerRegistry registry = sessionFactory().getServiceRegistry().getService( EventListenerRegistry.class ); + final CountingPostInsertTransactionBoundaryListener listener = new CountingPostInsertTransactionBoundaryListener(); + + registry.getEventListenerGroup( EventType.POST_INSERT ).appendListener( listener ); + + Session session = openSession(); + session.getTransaction().begin(); + StrTestEntity entity = new StrTestEntity( "str1" ); + session.save( entity ); + session.getTransaction().commit(); + session.close(); + + // Post insert listener invoked three times - before/after insertion of original data, + // revision entity and audit row. + Assert.assertEquals( 3, listener.getBeforeCount() ); + Assert.assertEquals( 3, listener.getAfterCount() ); + } + + private static class CountingPostInsertTransactionBoundaryListener implements PostInsertEventListener { + private final MutableInteger beforeCounter = new MutableInteger(); + private final MutableInteger afterCounter = new MutableInteger(); + + @Override + public void onPostInsert(PostInsertEvent event) { + event.getSession().getActionQueue().registerProcess(new BeforeTransactionCompletionProcess() { + @Override + public void doBeforeTransactionCompletion(SessionImplementor session) { + beforeCounter.increase(); + } + }); + event.getSession().getActionQueue().registerProcess(new AfterTransactionCompletionProcess() { + @Override + public void doAfterTransactionCompletion(boolean success, SessionImplementor session) { + afterCounter.increase(); + } + }); + } + + @Override + public boolean requiresPostCommitHanding(EntityPersister persister) { + return true; + } + + public int getBeforeCount() { + return beforeCounter.get(); + } + + public int getAfterCount() { + return afterCounter.get(); + } + } +}