Skip to content

Commit

Permalink
HHH-13915 : Prevent state sharing in basic proxies.
Browse files Browse the repository at this point in the history
Basic proxies were being created with a shared interceptor.  As the interceptor is stateful, this lead to cross-thread data leakage and thus intermittently broken persistence.
This mod creates an interceptor per proxy, preventing leaking of state.
  • Loading branch information
bighenry authored and jrenaat committed Jan 10, 2025
1 parent 41b4fe3 commit 0452d76
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public class BasicProxyFactoryImpl implements BasicProxyFactory {
private static final String PROXY_NAMING_SUFFIX = "HibernateBasicProxy";

private final Class proxyClass;
private final ProxyConfiguration.Interceptor interceptor;
private final Constructor proxyClassConstructor;

@SuppressWarnings({ "unchecked", "rawtypes" })
Expand Down Expand Up @@ -54,7 +53,7 @@ public BasicProxyFactoryImpl(final Class superClass, final Class interfaceClass,
.intercept( byteBuddyState.getProxyDefinitionHelpers().getInterceptorFieldAccessor() )
)
);
this.interceptor = new PassThroughInterceptor( proxyClass.getName() );

try {
proxyClassConstructor = proxyClass.getConstructor();
}
Expand All @@ -76,7 +75,9 @@ public Object getProxy() {
if ( proxyConfiguration == null ) {
throw new HibernateException( "Produced proxy does not correctly implement ProxyConfiguration" );
}
proxyConfiguration.$$_hibernate_set_interceptor( this.interceptor );
// Create a dedicated interceptor for the proxy. This is required as the interceptor is stateful.
final ProxyConfiguration.Interceptor interceptor = new PassThroughInterceptor( proxyClass.getName() );
proxyConfiguration.$$_hibernate_set_interceptor( interceptor );
return instance;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
import org.hibernate.testing.orm.junit.JiraKey;
import org.junit.Test;

@JiraKey(value = "HHH-12786")
public class ByteBuddyBasicProxyFactoryTest {

private static final BasicProxyFactoryImpl BASIC_PROXY_FACTORY = new BasicProxyFactoryImpl( Entity.class, null, new ByteBuddyState() );

@Test
@JiraKey(value = "HHH-12786")
public void testEqualsHashCode() {
Object entityProxy = BASIC_PROXY_FACTORY.getProxy();

Expand All @@ -30,13 +30,15 @@ public void testEqualsHashCode() {
}

@Test
@JiraKey(value = "HHH-12786")
public void testToString() {
Object entityProxy = BASIC_PROXY_FACTORY.getProxy();

assertTrue( entityProxy.toString().contains( "HibernateBasicProxy" ) );
}

@Test
@JiraKey(value = "HHH-12786")
public void testGetterSetter() {
Entity entityProxy = (Entity) BASIC_PROXY_FACTORY.getProxy();

Expand All @@ -50,12 +52,23 @@ public void testGetterSetter() {
}

@Test
@JiraKey(value = "HHH-12786")
public void testNonGetterSetterMethod() {
Entity entityProxy = (Entity) BASIC_PROXY_FACTORY.getProxy();

assertNull( entityProxy.otherMethod() );
}

@Test
@JiraKey(value = "HHH-13915")
public void testProxiesDoNotShareState() {
Entity entityAProxy = (Entity) BASIC_PROXY_FACTORY.getProxy();
entityAProxy.setString( "John Irving" );

Entity entityBProxy = (Entity) BASIC_PROXY_FACTORY.getProxy();
assertNull( entityBProxy.getString() );
}

public static class Entity {

private String string;
Expand Down

0 comments on commit 0452d76

Please sign in to comment.