Accessing real object from proxy fails

Hi,

I’m trying to get the real object from my proxy. I’m doing the following in my code:

Object unproxiedEntity = Hibernate.unproxy(instance);

However, this function returns the same proxy since this instance is not a HibernateProxy, but a HibernateBasicProxy. I’ve tried to find out why my instance is a HibernateBasicProxy and not a normal HibernateProxy, but I haven’t found anything. Searching for “HibernateBasicProxy” gives me almost no results online either. Any ideas why this is happening and how I can get the real object for this HibernateBasicProxy?

EDIT: We’re using ByteBuddy, so it might have to do with this. How do I get the real instance for a ByteBuddy proxy?

Thanks,
Erik

There’s no HibernateBasicProxy in Hibernate ORM codebase, so I’m not sure where you are getting that from.

Woops, I realized that HibernateBasicProxy is not actually the type but just a naming suffix. So basically what I have in my application are proxies generated by BasicProxyFactoryImpl:

public class BasicProxyFactoryImpl implements BasicProxyFactory {

	private static final Class[] NO_INTERFACES = new Class[0];
	private static final String PROXY_NAMING_SUFFIX = Environment.useLegacyProxyClassnames() ? "HibernateBasicProxy$" : "HibernateBasicProxy";

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

	@SuppressWarnings("unchecked")
	public BasicProxyFactoryImpl(Class superClass, Class[] interfaces, ByteBuddyState bytebuddy) {
		if ( superClass == null && ( interfaces == null || interfaces.length < 1 ) ) {
			throw new AssertionFailure( "attempting to build proxy without any superclass or interfaces" );
		}

		final Class<?> superClassOrMainInterface = superClass != null ? superClass : interfaces[0];

		this.proxyClass = bytebuddy.getCurrentyByteBuddy()
			.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 )
			.defineField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME, ProxyConfiguration.Interceptor.class, Visibility.PRIVATE )
			.method( ElementMatchers.isVirtual().and( ElementMatchers.not( ElementMatchers.isFinalizer() ) ) )
					.intercept( MethodDelegation.to( ProxyConfiguration.InterceptorDispatcher.class ) )
			.implement( ProxyConfiguration.class )
					.intercept( FieldAccessor.ofField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME ).withAssigner( Assigner.DEFAULT, Assigner.Typing.DYNAMIC ) )
			.make()
			.load( superClassOrMainInterface.getClassLoader(), ByteBuddyState.resolveClassLoadingStrategy( superClassOrMainInterface ) )
			.getLoaded();
		this.interceptor = new PassThroughInterceptor( proxyClass.getName() );
	}

	public Object getProxy() {
		try {
			final ProxyConfiguration proxy = (ProxyConfiguration) proxyClass.newInstance();
			proxy.$$_hibernate_set_interceptor( this.interceptor );
			return proxy;
		}
		catch (Throwable t) {
			throw new HibernateException( "Unable to instantiate proxy instance", t );
		}
	}

	public boolean isInstance(Object object) {
		return proxyClass.isInstance( object );
	}
}

The problem is that these proxies does not implement the HibernateProxy-interface, causing the unproxy-method to return the proxy again. I have tried manually adding the HibernateProxy-interface to my proxies like this:

static Object getProxyInstance(
			Class persistentClass,
			Set<Property> persistentProperties,
			ExtentManager extentManager)
			throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {

		if ( Modifier.isFinal( persistentClass.getModifiers() ) ) {
			// Use the default constructor, because final classes cannot be inherited.
			return useDefaultConstructor( persistentClass );
		}

		final ProxyConfiguration proxy = (ProxyConfiguration) Environment.getBytecodeProvider()
				.getProxyFactoryFactory()
				.buildBasicProxyFactory( persistentClass, new Class[] { TrackableEntity.class, HibernateProxy.class } )
				.getProxy();
		proxy.$$_hibernate_set_interceptor( new EntityProxyMethodHandler(
				proxy,
				persistentClass.getName(),
				persistentProperties,
				extentManager
		) );
		return proxy;
	}

However, if I do this then the hibernateProxy.getHibernateLazyInitializer() returns null instead. I need to access the real object somehow, because right now the MethodInterceptor gets stuck, causing an endless loop of calls to itself, resulting in StackOverflowException.

public class EntityProxyMethodHandler implements ProxyConfiguration.Interceptor, Serializable {

	private final EntityTracker entityTracker;
	private final Object proxiedObject;
	private final String proxiedClassName;
//	private AutofetchLazyInitializer initializer;

	EntityProxyMethodHandler(
			Object proxiedObject,
			String proxiedClassName,
			Set<Property> persistentProperties,
			ExtentManager extentManager) {
		this.proxiedObject = proxiedObject;
		this.proxiedClassName = proxiedClassName;
		this.entityTracker = new EntityTracker(persistentProperties, extentManager );
//		initializer = ((AutofetchLazyInitializer) ((HibernateProxy)proxiedObject).getHibernateLazyInitializer());
	}

	@Override
	@RuntimeType
	public Object intercept(@This Object instance, @Origin Method method, @AllArguments Object[] arguments)
			throws Exception {
		final String methodName = method.getName();

		if ( "toString".equals( methodName ) ) {
			return proxiedClassName + "@" + System.identityHashCode( instance );
		}
		else if ( "equals".equals( methodName ) ) {
			return proxiedObject == instance;
		}
		else if ( "hashCode".equals( methodName ) ) {
			return System.identityHashCode( instance );
		}
		else if ( arguments.length == 0 ) {
			switch ( methodName ) {
				case "disableTracking": {
					boolean oldValue = entityTracker.isTracking();
					entityTracker.setTracking( false );
					return oldValue;
				}
				case "enableTracking": {
					boolean oldValue = entityTracker.isTracking();
					entityTracker.setTracking( true );
					return oldValue;
				}
				case "isAccessed":
					return entityTracker.isAccessed();

				default:
					break;
			}
		}
		else if ( arguments.length == 1 ) {
			if ( methodName.equals( "addTracker" ) && method.getParameterTypes()[0].equals( Statistics.class ) ) {
				entityTracker.addTracker( (Statistics) arguments[0] );
				return null;
			}
			else if ( methodName.equals( "addTrackers" ) && method.getParameterTypes()[0].equals( Set.class ) ) {
				@SuppressWarnings("unchecked")
				Set<Statistics> newTrackers = (Set) arguments[0];
				entityTracker.addTrackers( newTrackers );
				return null;
			}
			else if ( methodName
					.equals( "extendProfile" ) && method.getParameterTypes()[0].equals( Statistics.class ) ) {
				entityTracker.extendProfile( (Statistics) arguments[0], instance );
				return null;
			}
			else if ( methodName
					.equals( "removeTracker" ) && method.getParameterTypes()[0].equals( Statistics.class ) ) {
				entityTracker.removeTracker( (Statistics) arguments[0] );
				return null;
			}
		}
		
//		Object unproxiedEntity = Hibernate.unproxy(proxiedObject);
//		Object realObject = initializer.getTarget();
		entityTracker.trackAccess( instance );
		// TODO (ammachado): Here the real instance should be used, but how?
		return method.invoke( instance, arguments );
	}
}

It seems like the MethodHandler intercepts everytime we call getHibernateLazyInitializer(), calling the same method again as seen in the callstack down below, causing the StackOverflow.
hibernateLazyInitializer
Any ideas? Maybe a methodfilter should be used here? The problem that my proxies does not have any LazyInitializer associated to them still persists. This started when we changed to ByteBuddy proxies.

I have done some investigation, and it seems like the created proxies in this case does not have a LazyInitializer associated to it (not implementing HibernateProxy-interface):

final ProxyConfiguration proxy = (ProxyConfiguration) Environment.getBytecodeProvider()
				.getProxyFactoryFactory()
				.buildBasicProxyFactory( persistentClass, new Class[] { TrackableEntity.class, HibernateProxy.class } )
				.getProxy();

However, there is another option, using the buildProxyFactory method instead of buildBasicProxyFactory. In this case we seem to return an instance of HibernateProxy, which is what I want. The problem is that in my case, I don’t have access to the ID of the proxy generated by the proxyfactory:

Environment.getBytecodeProvider().getProxyFactoryFactory().buildProxyFactory(sessionFactory).getProxy(id, session);

If anyone has an idea of how I could use the other option here or how I can modify the first option to have a LazyInitializer associated to the generated proxy (I need that for calling on the entity object instead of the proxy in my interceptor), it would be highly appreciated.