From 129530e464a406c97dcf3e0df31214f937449f53 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 25 Jul 2018 11:53:32 +0200 Subject: [PATCH] HHH-12857 Avoid using a static ByteBuddy object In the end, it does not change anything as the BytecodeProvider is still static but it helps as a general cleanup for the following commits. --- .../internal/bytebuddy/EnhancerImpl.java | 10 +- .../bytebuddy/BasicProxyFactoryImpl.java | 2 +- .../internal/bytebuddy/ByteBuddyState.java | 42 ++--- .../bytebuddy/BytecodeProviderImpl.java | 26 ++- .../bytebuddy/ProxyFactoryFactoryImpl.java | 14 +- .../pojo/bytebuddy/ByteBuddyProxyFactory.java | 142 +-------------- .../pojo/bytebuddy/ByteBuddyProxyHelper.java | 168 ++++++++++++++++++ .../pojo/bytebuddy/SerializableProxy.java | 12 +- .../ByteBuddyBasicProxyFactoryTest.java | 2 +- .../bytebuddy/EnhancerWildFlyNamesTest.java | 2 +- 10 files changed, 237 insertions(+), 183 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyHelper.java rename hibernate-core/src/test/java/org/hibernate/{test/proxy => bytecode/internal}/bytebuddy/ByteBuddyBasicProxyFactoryTest.java (97%) rename hibernate-core/src/test/java/org/hibernate/{test/bytecode/enhancement => bytecode/internal}/bytebuddy/EnhancerWildFlyNamesTest.java (97%) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java index 75fe3079c5..28fe6091cc 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java @@ -68,7 +68,7 @@ public class EnhancerImpl implements Enhancer { private static final CoreMessageLogger log = CoreLogging.messageLogger( Enhancer.class ); protected final ByteBuddyEnhancementContext enhancementContext; - private final ByteBuddyState bytebuddy; + private final ByteBuddyState byteBuddyState; private final TypePool classPool; @@ -77,11 +77,11 @@ public class EnhancerImpl implements Enhancer { * * @param enhancementContext Describes the context in which enhancement will occur so as to give access * to contextual/environmental information. - * @param bytebuddy refers to the ByteBuddy instance to use + * @param byteBuddyState refers to the ByteBuddy instance to use */ - public EnhancerImpl(final EnhancementContext enhancementContext, final ByteBuddyState bytebuddy) { + public EnhancerImpl(final EnhancementContext enhancementContext, final ByteBuddyState byteBuddyState) { this.enhancementContext = new ByteBuddyEnhancementContext( enhancementContext ); - this.bytebuddy = bytebuddy; + this.byteBuddyState = byteBuddyState; this.classPool = buildClassPool( this.enhancementContext ); } @@ -103,7 +103,7 @@ public class EnhancerImpl implements Enhancer { try { final TypeDescription managedCtClass = classPool.describe( safeClassName ).resolve(); DynamicType.Builder builder = doEnhance( - bytebuddy.getCurrentyByteBuddy().ignore( isDefaultFinalizer() ).redefine( managedCtClass, ClassFileLocator.Simple.of( safeClassName, originalBytes ) ), + byteBuddyState.getCurrentByteBuddy().ignore( isDefaultFinalizer() ).redefine( managedCtClass, ClassFileLocator.Simple.of( safeClassName, originalBytes ) ), managedCtClass ); if ( builder == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java index d9fe8a10ef..4933987969 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java @@ -36,7 +36,7 @@ public class BasicProxyFactoryImpl implements BasicProxyFactory { final Class superClassOrMainInterface = superClass != null ? superClass : interfaces[0]; - this.proxyClass = bytebuddy.getCurrentyByteBuddy() + this.proxyClass = bytebuddy.getCurrentByteBuddy() .with( new NamingStrategy.SuffixingRandom( PROXY_NAMING_SUFFIX, new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( superClassOrMainInterface.getName() ) ) ) .subclass( superClass == null ? Object.class : superClass, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR ) .implement( interfaces == null ? NO_INTERFACES : interfaces ) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java index 151fb514c2..56d6eaca1b 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java @@ -31,42 +31,34 @@ public final class ByteBuddyState { private static final CoreMessageLogger LOG = messageLogger( ByteBuddyProxyFactory.class ); - /** - * Ideally shouldn't be static but it has to until we can remove the - * deprecated static methods. - */ - private static final ByteBuddy buddy = new ByteBuddy().with( TypeValidation.DISABLED ); + private final ByteBuddy byteBuddy; /** - * This currently needs to be static: the BytecodeProvider is a static field of Environment and - * is being accessed from static methods. * It will be easier to maintain the cache and its state when it will no longer be static * in Hibernate ORM 6+. * Opted for WEAK keys to avoid leaking the classloader in case the SessionFactory isn't closed. * Avoiding Soft keys as they are prone to cause issues with unstable performance. */ - private static final TypeCache CACHE = new TypeCache.WithInlineExpunction( - TypeCache.Sort.WEAK ); + private final TypeCache typeCache; private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + ByteBuddyState() { + this.byteBuddy = new ByteBuddy().with( TypeValidation.DISABLED ); + this.typeCache = new TypeCache.WithInlineExpunction( TypeCache.Sort.WEAK ); + } + /** * Access to ByteBuddy. It's almost equivalent to creating a new ByteBuddy instance, * yet slightly preferrable so to be able to reuse the same instance. * @return */ - public ByteBuddy getCurrentyByteBuddy() { - return buddy; + public ByteBuddy getCurrentByteBuddy() { + return byteBuddy; } - /** - * @deprecated as we should not need static access to this state. - * This will be removed with no replacement. - * It's actually likely that this whole class becomes unnecessary in the near future. - */ - @Deprecated - public static TypeCache getCacheForProxies() { - return CACHE; + public TypeCache getCacheForProxies() { + return typeCache; } /** @@ -78,17 +70,7 @@ public final class ByteBuddyState { * of re-creating the small helpers should be negligible. */ void clearState() { - CACHE.clear(); - } - - /** - * @deprecated as we should not need static access to this state. - * This will be removed with no replacement. - * It's actually likely that this whole class becomes unnecessary in the near future. - */ - @Deprecated - public static ByteBuddy getStaticByteBuddyInstance() { - return buddy; + typeCache.clear(); } public static ClassLoadingStrategy resolveClassLoadingStrategy(Class originalClass) { diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BytecodeProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BytecodeProviderImpl.java index 68422d10af..d1c589e51b 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BytecodeProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BytecodeProviderImpl.java @@ -18,6 +18,7 @@ import org.hibernate.bytecode.enhance.spi.Enhancer; import org.hibernate.bytecode.spi.BytecodeProvider; import org.hibernate.bytecode.spi.ProxyFactoryFactory; import org.hibernate.bytecode.spi.ReflectionOptimizer; +import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyHelper; import net.bytebuddy.NamingStrategy; import net.bytebuddy.description.method.MethodDescription; @@ -37,7 +38,6 @@ import net.bytebuddy.matcher.ElementMatchers; public class BytecodeProviderImpl implements BytecodeProvider { - private static final ByteBuddyState bytebuddy = new ByteBuddyState(); private static final String INSTANTIATOR_PROXY_NAMING_SUFFIX = "HibernateInstantiator"; private static final String OPTIMIZER_PROXY_NAMING_SUFFIX = "HibernateAccessOptimizer"; private static final ElementMatcher.Junction newInstanceMethodName = ElementMatchers.named( "newInstance" ); @@ -45,9 +45,18 @@ public class BytecodeProviderImpl implements BytecodeProvider { private static final ElementMatcher.Junction setPropertyValuesMethodName = ElementMatchers.named( "setPropertyValues" ); private static final ElementMatcher.Junction getPropertyNamesMethodName = ElementMatchers.named( "getPropertyNames" ); + private final ByteBuddyState byteBuddyState; + + private final ByteBuddyProxyHelper byteBuddyProxyHelper; + + public BytecodeProviderImpl() { + this.byteBuddyState = new ByteBuddyState(); + this.byteBuddyProxyHelper = new ByteBuddyProxyHelper( byteBuddyState ); + } + @Override public ProxyFactoryFactory getProxyFactoryFactory() { - return new ProxyFactoryFactoryImpl( bytebuddy ); + return new ProxyFactoryFactoryImpl( byteBuddyState, byteBuddyProxyHelper ); } @Override @@ -61,7 +70,7 @@ public class BytecodeProviderImpl implements BytecodeProvider { // we only provide a fast class instantiator if the class can be instantiated final Constructor constructor = findConstructor( clazz ); - fastClass = bytebuddy.getCurrentyByteBuddy() + fastClass = byteBuddyState.getCurrentByteBuddy() .with( new NamingStrategy.SuffixingRandom( INSTANTIATOR_PROXY_NAMING_SUFFIX, new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( clazz.getName() ) ) ) .subclass( ReflectionOptimizer.InstantiationOptimizer.class ) @@ -79,7 +88,7 @@ public class BytecodeProviderImpl implements BytecodeProvider { final Method[] setters = new Method[setterNames.length]; findAccessors( clazz, getterNames, setterNames, types, getters, setters ); - final Class bulkAccessor = bytebuddy.getCurrentyByteBuddy() + final Class bulkAccessor = byteBuddyState.getCurrentByteBuddy() .with( new NamingStrategy.SuffixingRandom( OPTIMIZER_PROXY_NAMING_SUFFIX, new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( clazz.getName() ) ) ) .subclass( ReflectionOptimizer.AccessOptimizer.class ) @@ -104,6 +113,10 @@ public class BytecodeProviderImpl implements BytecodeProvider { } } + public ByteBuddyProxyHelper getByteBuddyProxyHelper() { + return byteBuddyProxyHelper; + } + private static class GetPropertyValues implements ByteCodeAppender { private final Class clazz; @@ -271,11 +284,12 @@ public class BytecodeProviderImpl implements BytecodeProvider { @Override public Enhancer getEnhancer(EnhancementContext enhancementContext) { - return new EnhancerImpl( enhancementContext, bytebuddy ); + return new EnhancerImpl( enhancementContext, byteBuddyState ); } + @Override public void resetCaches() { - bytebuddy.clearState(); + byteBuddyState.clearState(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ProxyFactoryFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ProxyFactoryFactoryImpl.java index 9df70b85b1..0f68aead89 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ProxyFactoryFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ProxyFactoryFactoryImpl.java @@ -11,22 +11,26 @@ import org.hibernate.bytecode.spi.ProxyFactoryFactory; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.proxy.ProxyFactory; import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyFactory; +import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyHelper; public class ProxyFactoryFactoryImpl implements ProxyFactoryFactory { - private final ByteBuddyState bytebuddy; + private final ByteBuddyState byteBuddyState; - public ProxyFactoryFactoryImpl(ByteBuddyState bytebuddy) { - this.bytebuddy = bytebuddy; + private final ByteBuddyProxyHelper byteBuddyProxyHelper; + + public ProxyFactoryFactoryImpl(ByteBuddyState byteBuddyState, ByteBuddyProxyHelper byteBuddyProxyHelper) { + this.byteBuddyState = byteBuddyState; + this.byteBuddyProxyHelper = byteBuddyProxyHelper; } @Override public ProxyFactory buildProxyFactory(SessionFactoryImplementor sessionFactory) { - return new ByteBuddyProxyFactory(); + return new ByteBuddyProxyFactory( byteBuddyProxyHelper ); } @Override public BasicProxyFactory buildBasicProxyFactory(Class superClass, Class[] interfaces) { - return new BasicProxyFactoryImpl( superClass, interfaces, bytebuddy ); + return new BasicProxyFactoryImpl( superClass, interfaces, byteBuddyState ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyFactory.java b/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyFactory.java index 4c4173b420..d67b9720c6 100644 --- a/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyFactory.java @@ -6,16 +6,13 @@ */ package org.hibernate.proxy.pojo.bytebuddy; +import static org.hibernate.internal.CoreLogging.messageLogger; + import java.io.Serializable; import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Locale; import java.util.Set; import org.hibernate.HibernateException; -import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState; import org.hibernate.cfg.Environment; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.CoreMessageLogger; @@ -26,28 +23,11 @@ import org.hibernate.proxy.ProxyConfiguration; import org.hibernate.proxy.ProxyFactory; import org.hibernate.type.CompositeType; -import net.bytebuddy.NamingStrategy; -import net.bytebuddy.TypeCache; -import net.bytebuddy.description.modifier.Visibility; -import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; -import net.bytebuddy.implementation.FieldAccessor; -import net.bytebuddy.implementation.MethodDelegation; -import net.bytebuddy.implementation.SuperMethodCall; -import net.bytebuddy.implementation.bytecode.assign.Assigner; - -import static net.bytebuddy.matcher.ElementMatchers.isFinalizer; -import static net.bytebuddy.matcher.ElementMatchers.isSynthetic; -import static net.bytebuddy.matcher.ElementMatchers.isVirtual; -import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.not; -import static net.bytebuddy.matcher.ElementMatchers.returns; -import static org.hibernate.internal.CoreLogging.messageLogger; - public class ByteBuddyProxyFactory implements ProxyFactory, Serializable { private static final CoreMessageLogger LOG = messageLogger( ByteBuddyProxyFactory.class ); - private static final String PROXY_NAMING_SUFFIX = Environment.useLegacyProxyClassnames() ? "HibernateProxy$" : "HibernateProxy"; + + private final ByteBuddyProxyHelper byteBuddyProxyHelper; private Class persistentClass; private String entityName; @@ -59,6 +39,10 @@ public class ByteBuddyProxyFactory implements ProxyFactory, Serializable { private Class proxyClass; + public ByteBuddyProxyFactory(ByteBuddyProxyHelper byteBuddyProxyHelper) { + this.byteBuddyProxyHelper = byteBuddyProxyHelper; + } + @Override public void postInstantiate( String entityName, @@ -75,7 +59,7 @@ public class ByteBuddyProxyFactory implements ProxyFactory, Serializable { this.componentIdType = componentIdType; this.overridesEquals = ReflectHelper.overridesEquals( persistentClass ); - this.proxyClass = buildProxy( persistentClass, this.interfaces ); + this.proxyClass = byteBuddyProxyHelper.buildProxy( persistentClass, this.interfaces ); } private Class[] toArray(Set interfaces) { @@ -86,35 +70,6 @@ public class ByteBuddyProxyFactory implements ProxyFactory, Serializable { return interfaces.toArray( new Class[interfaces.size()] ); } - public static Class buildProxy( - final Class persistentClass, - final Class[] interfaces) { - Set> key = new HashSet>(); - if ( interfaces.length == 1 ) { - key.add( persistentClass ); - } - key.addAll( Arrays.>asList( interfaces ) ); - - final TypeCache cacheForProxies = ByteBuddyState.getCacheForProxies(); - - return cacheForProxies.findOrInsert( persistentClass.getClassLoader(), new TypeCache.SimpleKey(key), () -> - ByteBuddyState.getStaticByteBuddyInstance() - .ignore( isSynthetic().and( named( "getMetaClass" ).and( returns( td -> "groovy.lang.MetaClass".equals( td.getName() ) ) ) ) ) - .with( new NamingStrategy.SuffixingRandom( PROXY_NAMING_SUFFIX, new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( persistentClass.getName() ) ) ) - .subclass( interfaces.length == 1 ? persistentClass : Object.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING ) - .implement( (Type[]) interfaces ) - .method( isVirtual().and( not( isFinalizer() ) ) ) - .intercept( MethodDelegation.to( ProxyConfiguration.InterceptorDispatcher.class ) ) - .method( nameStartsWith( "$$_hibernate_" ).and( isVirtual() ) ) - .intercept( SuperMethodCall.INSTANCE ) - .defineField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME, ProxyConfiguration.Interceptor.class, Visibility.PRIVATE ) - .implement( ProxyConfiguration.class ) - .intercept( FieldAccessor.ofField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME ).withAssigner( Assigner.DEFAULT, Assigner.Typing.DYNAMIC ) ) - .make() - .load( persistentClass.getClassLoader(), ByteBuddyState.resolveClassLoadingStrategy( persistentClass ) ) - .getLoaded(), cacheForProxies ); - } - @Override public HibernateProxy getProxy( Serializable id, @@ -142,83 +97,4 @@ public class ByteBuddyProxyFactory implements ProxyFactory, Serializable { throw new HibernateException( LOG.bytecodeEnhancementFailed( entityName ), t ); } } - - public static HibernateProxy deserializeProxy(SerializableProxy serializableProxy) { - final ByteBuddyInterceptor interceptor = new ByteBuddyInterceptor( - serializableProxy.getEntityName(), - serializableProxy.getPersistentClass(), - serializableProxy.getInterfaces(), - serializableProxy.getId(), - resolveIdGetterMethod( serializableProxy ), - resolveIdSetterMethod( serializableProxy ), - serializableProxy.getComponentIdType(), - null, - ReflectHelper.overridesEquals( serializableProxy.getPersistentClass() ) - ); - - // note: interface is assumed to already contain HibernateProxy.class - try { - final Class proxyClass = buildProxy( - serializableProxy.getPersistentClass(), - serializableProxy.getInterfaces() - ); - final HibernateProxy proxy = (HibernateProxy) proxyClass.newInstance(); - ( (ProxyConfiguration) proxy ).$$_hibernate_set_interceptor( interceptor ); - return proxy; - } - catch (Throwable t) { - final String message = LOG.bytecodeEnhancementFailed( serializableProxy.getEntityName() ); - LOG.error( message, t ); - throw new HibernateException( message, t ); - } - } - - @SuppressWarnings("unchecked") - private static Method resolveIdGetterMethod(SerializableProxy serializableProxy) { - if ( serializableProxy.getIdentifierGetterMethodName() == null ) { - return null; - } - - try { - return serializableProxy.getIdentifierGetterMethodClass().getDeclaredMethod( serializableProxy.getIdentifierGetterMethodName() ); - } - catch (NoSuchMethodException e) { - throw new HibernateException( - String.format( - Locale.ENGLISH, - "Unable to deserialize proxy [%s, %s]; could not locate id getter method [%s] on entity class [%s]", - serializableProxy.getEntityName(), - serializableProxy.getId(), - serializableProxy.getIdentifierGetterMethodName(), - serializableProxy.getIdentifierGetterMethodClass() - ) - ); - } - } - - @SuppressWarnings("unchecked") - private static Method resolveIdSetterMethod(SerializableProxy serializableProxy) { - if ( serializableProxy.getIdentifierSetterMethodName() == null ) { - return null; - } - - try { - return serializableProxy.getIdentifierSetterMethodClass().getDeclaredMethod( - serializableProxy.getIdentifierSetterMethodName(), - serializableProxy.getIdentifierSetterMethodParams() - ); - } - catch (NoSuchMethodException e) { - throw new HibernateException( - String.format( - Locale.ENGLISH, - "Unable to deserialize proxy [%s, %s]; could not locate id setter method [%s] on entity class [%s]", - serializableProxy.getEntityName(), - serializableProxy.getId(), - serializableProxy.getIdentifierSetterMethodName(), - serializableProxy.getIdentifierSetterMethodClass() - ) - ); - } - } } diff --git a/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyHelper.java b/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyHelper.java new file mode 100644 index 0000000000..b453d76d4d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyHelper.java @@ -0,0 +1,168 @@ +/* + * 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 . + */ +package org.hibernate.proxy.pojo.bytebuddy; + +import static net.bytebuddy.matcher.ElementMatchers.isFinalizer; +import static net.bytebuddy.matcher.ElementMatchers.isSynthetic; +import static net.bytebuddy.matcher.ElementMatchers.isVirtual; +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.returns; +import static org.hibernate.internal.CoreLogging.messageLogger; + +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +import org.hibernate.HibernateException; +import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState; +import org.hibernate.cfg.Environment; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.internal.util.collections.ArrayHelper; +import org.hibernate.proxy.HibernateProxy; +import org.hibernate.proxy.ProxyConfiguration; +import org.hibernate.proxy.ProxyFactory; +import org.hibernate.type.CompositeType; + +import net.bytebuddy.NamingStrategy; +import net.bytebuddy.TypeCache; +import net.bytebuddy.description.modifier.Visibility; +import net.bytebuddy.dynamic.DynamicType.Unloaded; +import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; +import net.bytebuddy.implementation.FieldAccessor; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.SuperMethodCall; +import net.bytebuddy.implementation.bytecode.assign.Assigner; + +public class ByteBuddyProxyHelper implements Serializable { + + private static final CoreMessageLogger LOG = messageLogger( ByteBuddyProxyHelper.class ); + private static final String PROXY_NAMING_SUFFIX = Environment.useLegacyProxyClassnames() ? "HibernateProxy$" : "HibernateProxy"; + + private final ByteBuddyState byteBuddyState; + + public ByteBuddyProxyHelper(ByteBuddyState byteBuddyState) { + this.byteBuddyState = byteBuddyState; + } + + public Class buildProxy( + final Class persistentClass, + final Class[] interfaces) { + Set> key = new HashSet>(); + if ( interfaces.length == 1 ) { + key.add( persistentClass ); + } + key.addAll( Arrays.>asList( interfaces ) ); + + final TypeCache cacheForProxies = byteBuddyState.getCacheForProxies(); + + return cacheForProxies.findOrInsert( persistentClass.getClassLoader(), new TypeCache.SimpleKey(key), () -> + byteBuddyState.getCurrentByteBuddy() + .ignore( isSynthetic().and( named( "getMetaClass" ).and( returns( td -> "groovy.lang.MetaClass".equals( td.getName() ) ) ) ) ) + .with( new NamingStrategy.SuffixingRandom( PROXY_NAMING_SUFFIX, new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( persistentClass.getName() ) ) ) + .subclass( interfaces.length == 1 ? persistentClass : Object.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING ) + .implement( (Type[]) interfaces ) + .method( isVirtual().and( not( isFinalizer() ) ) ) + .intercept( MethodDelegation.to( ProxyConfiguration.InterceptorDispatcher.class ) ) + .method( nameStartsWith( "$$_hibernate_" ).and( isVirtual() ) ) + .intercept( SuperMethodCall.INSTANCE ) + .defineField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME, ProxyConfiguration.Interceptor.class, Visibility.PRIVATE ) + .implement( ProxyConfiguration.class ) + .intercept( FieldAccessor.ofField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME ).withAssigner( Assigner.DEFAULT, Assigner.Typing.DYNAMIC ) ) + .make() + .load( persistentClass.getClassLoader(), ByteBuddyState.resolveClassLoadingStrategy( persistentClass ) ) + .getLoaded(), cacheForProxies ); + } + + public HibernateProxy deserializeProxy(SerializableProxy serializableProxy) { + final ByteBuddyInterceptor interceptor = new ByteBuddyInterceptor( + serializableProxy.getEntityName(), + serializableProxy.getPersistentClass(), + serializableProxy.getInterfaces(), + serializableProxy.getId(), + resolveIdGetterMethod( serializableProxy ), + resolveIdSetterMethod( serializableProxy ), + serializableProxy.getComponentIdType(), + null, + ReflectHelper.overridesEquals( serializableProxy.getPersistentClass() ) + ); + + // note: interface is assumed to already contain HibernateProxy.class + try { + final Class proxyClass = buildProxy( + serializableProxy.getPersistentClass(), + serializableProxy.getInterfaces() + ); + final HibernateProxy proxy = (HibernateProxy) proxyClass.newInstance(); + ( (ProxyConfiguration) proxy ).$$_hibernate_set_interceptor( interceptor ); + return proxy; + } + catch (Throwable t) { + final String message = LOG.bytecodeEnhancementFailed( serializableProxy.getEntityName() ); + LOG.error( message, t ); + throw new HibernateException( message, t ); + } + } + + @SuppressWarnings("unchecked") + private static Method resolveIdGetterMethod(SerializableProxy serializableProxy) { + if ( serializableProxy.getIdentifierGetterMethodName() == null ) { + return null; + } + + try { + return serializableProxy.getIdentifierGetterMethodClass().getDeclaredMethod( serializableProxy.getIdentifierGetterMethodName() ); + } + catch (NoSuchMethodException e) { + throw new HibernateException( + String.format( + Locale.ENGLISH, + "Unable to deserialize proxy [%s, %s]; could not locate id getter method [%s] on entity class [%s]", + serializableProxy.getEntityName(), + serializableProxy.getId(), + serializableProxy.getIdentifierGetterMethodName(), + serializableProxy.getIdentifierGetterMethodClass() + ) + ); + } + } + + @SuppressWarnings("unchecked") + private static Method resolveIdSetterMethod(SerializableProxy serializableProxy) { + if ( serializableProxy.getIdentifierSetterMethodName() == null ) { + return null; + } + + try { + return serializableProxy.getIdentifierSetterMethodClass().getDeclaredMethod( + serializableProxy.getIdentifierSetterMethodName(), + serializableProxy.getIdentifierSetterMethodParams() + ); + } + catch (NoSuchMethodException e) { + throw new HibernateException( + String.format( + Locale.ENGLISH, + "Unable to deserialize proxy [%s, %s]; could not locate id setter method [%s] on entity class [%s]", + serializableProxy.getEntityName(), + serializableProxy.getId(), + serializableProxy.getIdentifierSetterMethodName(), + serializableProxy.getIdentifierSetterMethodClass() + ) + ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/SerializableProxy.java b/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/SerializableProxy.java index 0d9839af4d..546247f606 100644 --- a/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/SerializableProxy.java +++ b/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/SerializableProxy.java @@ -9,6 +9,11 @@ package org.hibernate.proxy.pojo.bytebuddy; import java.io.Serializable; import java.lang.reflect.Method; +import org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl; +import org.hibernate.bytecode.internal.bytebuddy.ProxyFactoryFactoryImpl; +import org.hibernate.bytecode.spi.BytecodeProvider; +import org.hibernate.bytecode.spi.ProxyFactoryFactory; +import org.hibernate.cfg.Environment; import org.hibernate.proxy.AbstractSerializableProxy; import org.hibernate.proxy.HibernateProxy; import org.hibernate.type.CompositeType; @@ -125,7 +130,12 @@ public final class SerializableProxy extends AbstractSerializableProxy { } private Object readResolve() { - HibernateProxy proxy = ByteBuddyProxyFactory.deserializeProxy( this ); + BytecodeProvider bytecodeProvider = Environment.getBytecodeProvider(); + if ( !( bytecodeProvider instanceof BytecodeProviderImpl ) ) { + throw new IllegalStateException( "The bytecode provider is not ByteBuddy, unable to deserialize a ByteBuddy proxy." ); + } + + HibernateProxy proxy = ( (BytecodeProviderImpl) bytecodeProvider ).getByteBuddyProxyHelper().deserializeProxy( this ); afterDeserialization( (ByteBuddyInterceptor) proxy.getHibernateLazyInitializer() ); return proxy; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/proxy/bytebuddy/ByteBuddyBasicProxyFactoryTest.java b/hibernate-core/src/test/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyBasicProxyFactoryTest.java similarity index 97% rename from hibernate-core/src/test/java/org/hibernate/test/proxy/bytebuddy/ByteBuddyBasicProxyFactoryTest.java rename to hibernate-core/src/test/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyBasicProxyFactoryTest.java index 8caeac93d4..afc655116b 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/proxy/bytebuddy/ByteBuddyBasicProxyFactoryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyBasicProxyFactoryTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.proxy.bytebuddy; +package org.hibernate.bytecode.internal.bytebuddy; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/bytebuddy/EnhancerWildFlyNamesTest.java b/hibernate-core/src/test/java/org/hibernate/bytecode/internal/bytebuddy/EnhancerWildFlyNamesTest.java similarity index 97% rename from hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/bytebuddy/EnhancerWildFlyNamesTest.java rename to hibernate-core/src/test/java/org/hibernate/bytecode/internal/bytebuddy/EnhancerWildFlyNamesTest.java index a049093956..cb8ddaf6d6 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/bytebuddy/EnhancerWildFlyNamesTest.java +++ b/hibernate-core/src/test/java/org/hibernate/bytecode/internal/bytebuddy/EnhancerWildFlyNamesTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.bytecode.enhancement.bytebuddy; +package org.hibernate.bytecode.internal.bytebuddy; import java.io.ByteArrayOutputStream; import java.io.IOException;