Skip to content

Commit

Permalink
HHH-14540 Don't share session-scoped interceptors with temp session
Browse files Browse the repository at this point in the history
  • Loading branch information
Naros authored and Sanne committed Nov 16, 2021
1 parent 3845d2f commit e155fc5
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 3 deletions.
Expand Up @@ -1123,7 +1123,7 @@ public Type resolveParameterBindType(Class clazz){
}
}

public static Interceptor configuredInterceptor(Interceptor interceptor, SessionFactoryOptions options) {
public static Interceptor configuredInterceptor(Interceptor interceptor, boolean explicitNoInterceptor, SessionFactoryOptions options) {
// NOTE : DO NOT return EmptyInterceptor.INSTANCE from here as a "default for the Session"
// we "filter" that one out here. The return from here should represent the
// explicitly configured Interceptor (if one). Return null from here instead; Session
Expand All @@ -1139,6 +1139,12 @@ public static Interceptor configuredInterceptor(Interceptor interceptor, Session
return optionsInterceptor;
}

// If explicitly asking for no interceptor and there is no SessionFactory-scoped interceptors, then
// no need to inherit from the configured stateless session ones.
if ( explicitNoInterceptor ) {
return null;
}

// then check the Session-scoped interceptor prototype
final Class<? extends Interceptor> statelessInterceptorImplementor = options.getStatelessInterceptorImplementor();
final Supplier<? extends Interceptor> statelessInterceptorImplementorSupplier = options.getStatelessInterceptorImplementorSupplier();
Expand Down Expand Up @@ -1181,6 +1187,7 @@ public static class SessionBuilderImpl<T extends SessionBuilder> implements Sess
private String tenantIdentifier;
private TimeZone jdbcTimeZone;
private boolean queryParametersValidationEnabled;
private boolean explicitNoInterceptor;

// Lazy: defaults can be built by invoking the builder in fastSessionServices.defaultSessionEventListeners
// (Need a fresh build for each Session as the listener instances can't be reused across sessions)
Expand Down Expand Up @@ -1269,7 +1276,7 @@ public Connection getConnection() {

@Override
public Interceptor getInterceptor() {
return configuredInterceptor( interceptor, sessionFactory.getSessionFactoryOptions() );
return configuredInterceptor( interceptor, explicitNoInterceptor, sessionFactory.getSessionFactoryOptions() );
}

@Override
Expand Down Expand Up @@ -1316,13 +1323,15 @@ public T owner(SessionOwner sessionOwner) {
@SuppressWarnings("unchecked")
public T interceptor(Interceptor interceptor) {
this.interceptor = interceptor;
this.explicitNoInterceptor = false;
return (T) this;
}

@Override
@SuppressWarnings("unchecked")
public T noInterceptor() {
this.interceptor = EmptyInterceptor.INSTANCE;
this.explicitNoInterceptor = true;
return (T) this;
}

Expand Down Expand Up @@ -1494,7 +1503,7 @@ public Connection getConnection() {

@Override
public Interceptor getInterceptor() {
return configuredInterceptor( EmptyInterceptor.INSTANCE, sessionFactory.getSessionFactoryOptions() );
return configuredInterceptor( EmptyInterceptor.INSTANCE, false, sessionFactory.getSessionFactoryOptions() );

}

Expand Down
Expand Up @@ -160,6 +160,7 @@ public void doBeforeTransactionCompletion(SessionImplementor session) {
.connection()
.autoClose( false )
.connectionHandlingMode( PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION )
.noInterceptor()
.openSession();
executeInSession( temporarySession );
temporarySession.flush();
Expand Down
@@ -0,0 +1,76 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.test.integration.tm;

import static org.junit.Assert.assertEquals;

import java.util.Map;

import javax.persistence.EntityManager;
import javax.transaction.TransactionManager;

import org.hibernate.FlushMode;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase;
import org.hibernate.envers.test.Priority;
import org.hibernate.envers.test.entities.StrTestEntity;
import org.hibernate.internal.SessionImpl;
import org.junit.Test;

import org.hibernate.testing.jta.TestingJtaBootstrap;
import org.hibernate.testing.jta.TestingJtaPlatformImpl;

/**
* @author Chris Cranford
*/
public class SessionFactoryInterceptorTransactionTest extends BaseEnversJPAFunctionalTestCase {

private TestInterceptor interceptor;
private TransactionManager tm;

@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { StrTestEntity.class };
}

@Override
protected void addConfigOptions(Map options) {
super.addConfigOptions( options );

TestInterceptor.reset();

this.interceptor = new TestInterceptor();
options.put( AvailableSettings.INTERCEPTOR, interceptor );
options.put( AvailableSettings.ALLOW_JTA_TRANSACTION_ACCESS, true );

TestingJtaBootstrap.prepare( options );
tm = TestingJtaPlatformImpl.INSTANCE.getTransactionManager();
}

@Test
@Priority(10)
public void initData() throws Exception {
// Revision 1
EntityManager em = getEntityManager();
// Explicitly use manual flush to trigger separate temporary session write via Envers
em.unwrap( SessionImpl.class ).setHibernateFlushMode( FlushMode.MANUAL );
tm.begin();
StrTestEntity entity = new StrTestEntity( "Test" );
em.persist( entity );
em.flush();
tm.commit();
}

@Test
public void testInterceptorInvocations() throws Exception {
// Expect the interceptor to have been created once and invoked twice, once for the original session
// and follow-up for the Envers temporary session.
final Map<TestInterceptor, Integer> invocationMap = TestInterceptor.getBeforeCompletionCallbacks();
assertEquals( 1, invocationMap.size() );
assertEquals( invocationMap.values().stream().filter( v -> v == 2 ).count(), 1 );
}
}
@@ -0,0 +1,73 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.test.integration.tm;

import static org.junit.Assert.assertEquals;

import java.util.Map;

import javax.persistence.EntityManager;
import javax.transaction.TransactionManager;

import org.hibernate.FlushMode;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase;
import org.hibernate.envers.test.Priority;
import org.hibernate.envers.test.entities.StrTestEntity;
import org.hibernate.internal.SessionImpl;
import org.junit.Test;

import org.hibernate.testing.jta.TestingJtaBootstrap;
import org.hibernate.testing.jta.TestingJtaPlatformImpl;

/**
* @author Chris Cranford
*/
public class SessionInterceptorTransactionTest extends BaseEnversJPAFunctionalTestCase {

private TransactionManager tm;

@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { StrTestEntity.class };
}

@Override
protected void addConfigOptions(Map options) {
super.addConfigOptions( options );

TestInterceptor.reset();

options.put( AvailableSettings.SESSION_SCOPED_INTERCEPTOR, TestInterceptor.class.getName() );
options.put( AvailableSettings.ALLOW_JTA_TRANSACTION_ACCESS, "true" );

TestingJtaBootstrap.prepare( options );
tm = TestingJtaPlatformImpl.INSTANCE.getTransactionManager();
}

@Test
@Priority(10)
public void initData() throws Exception {
// Revision 1
EntityManager em = getEntityManager();
// Explicitly use manual flush to trigger separate temporary session write via Envers
em.unwrap( SessionImpl.class ).setHibernateFlushMode( FlushMode.MANUAL );
tm.begin();
StrTestEntity entity = new StrTestEntity( "Test" );
em.persist( entity );
em.flush();
tm.commit();
}

@Test
public void testInterceptorInvocations() throws Exception {
// The interceptor should only be created once and should only be invoked once.
final Map<TestInterceptor, Integer> invocationMap = TestInterceptor.getBeforeCompletionCallbacks();
assertEquals( 1, invocationMap.size() );
assertEquals( invocationMap.values().stream().filter( v -> v == 1 ).count(), 1 );
}
}
@@ -0,0 +1,43 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.test.integration.tm;

import java.util.HashMap;
import java.util.Map;

import org.hibernate.EmptyInterceptor;
import org.hibernate.Transaction;

import org.jboss.logging.Logger;

/**
* @author Chris Cranford
*/
public class TestInterceptor extends EmptyInterceptor {

private static final Logger LOGGER = Logger.getLogger( TestInterceptor.class );
private static Map<TestInterceptor, Integer> interceptorInvocations = new HashMap<>();

public TestInterceptor() {
interceptorInvocations.put( this, 0 );
}

@Override
public void beforeTransactionCompletion(Transaction tx) {
super.beforeTransactionCompletion(tx);
interceptorInvocations.put( this, interceptorInvocations.get( this ) + 1 );
LOGGER.info( "Interceptor beforeTransactionCompletion invoked" );
}

public static Map<TestInterceptor, Integer> getBeforeCompletionCallbacks() {
return interceptorInvocations;
}

public static void reset() {
interceptorInvocations.clear();
}
}

0 comments on commit e155fc5

Please sign in to comment.