All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.jpa.event.internal.jpa.CallbackBuilderLegacyImpl Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * 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.jpa.event.internal.jpa;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.ExcludeDefaultListeners;
import javax.persistence.ExcludeSuperclassListeners;
import javax.persistence.MappedSuperclass;
import javax.persistence.PersistenceException;

import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.ClassLoadingException;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XMethod;
import org.hibernate.jpa.event.spi.jpa.Callback;
import org.hibernate.jpa.event.spi.jpa.CallbackType;
import org.hibernate.jpa.event.spi.jpa.CallbackBuilder;
import org.hibernate.jpa.event.spi.jpa.ListenerFactory;

import org.jboss.logging.Logger;

/**
 * EntityCallbackBuilder implementation using HCANN ReflectionManager.  "legacy" in that
 * we want to move to Jandex instead.
 *
 * @author Steve Ebersole
 */
public class CallbackBuilderLegacyImpl implements CallbackBuilder {
	private static final Logger log = Logger.getLogger( CallbackBuilderLegacyImpl.class );

	private final ListenerFactory jpaListenerFactory;
	private final ReflectionManager reflectionManager;

	public CallbackBuilderLegacyImpl(ListenerFactory jpaListenerFactory, ReflectionManager reflectionManager) {
		this.jpaListenerFactory = jpaListenerFactory;
		this.reflectionManager = reflectionManager;
	}

	@Override
	public void buildCallbacksForEntity(String entityClassName, CallbackRegistrar callbackRegistrar) {
		try {
			final XClass entityXClass = reflectionManager.classForName( entityClassName );
			final Class entityClass = reflectionManager.toClass( entityXClass );
			for ( CallbackType callbackType : CallbackType.values() ) {
				if ( callbackRegistrar.hasRegisteredCallbacks( entityClass, callbackType ) ) {
					// this most likely means we have a class mapped multiple times using the hbm.xml
					// "entity name" feature
					log.debugf(
							"CallbackRegistry reported that Class [%s] already had %s callbacks registered; " +
									"assuming this means the class was mapped twice " +
									"(using hbm.xml entity-name support) - skipping subsequent registrations",
							entityClassName,
							callbackType.getCallbackAnnotation().getSimpleName()
					);
					continue;
				}
				final Callback[] callbacks = resolveCallbacks( entityXClass, callbackType, reflectionManager );
				callbackRegistrar.registerCallbacks( entityClass, callbacks );
			}
		}
		catch (ClassLoadingException e) {
			throw new MappingException( "entity class not found: " + entityClassName, e );
		}
	}

	@Override
	public void release() {
		// nothign to do
	}

	public Callback[] resolveCallbacks(XClass beanClass, CallbackType callbackType, ReflectionManager reflectionManager) {
		List callbacks = new ArrayList();
		List callbacksMethodNames = new ArrayList(); //used to track overridden methods
		List orderedListeners = new ArrayList();
		XClass currentClazz = beanClass;
		boolean stopListeners = false;
		boolean stopDefaultListeners = false;
		do {
			Callback callback = null;
			List methods = currentClazz.getDeclaredMethods();
			for ( final XMethod xMethod : methods ) {
				if ( xMethod.isAnnotationPresent( callbackType.getCallbackAnnotation() ) ) {
					Method method = reflectionManager.toMethod( xMethod );
					final String methodName = method.getName();
					if ( !callbacksMethodNames.contains( methodName ) ) {
						//overridden method, remove the superclass overridden method
						if ( callback == null ) {
							callback = new EntityCallback( method, callbackType );
							Class returnType = method.getReturnType();
							Class[] args = method.getParameterTypes();
							if ( returnType != Void.TYPE || args.length != 0 ) {
								throw new RuntimeException(
										"Callback methods annotated on the bean class must return void and take no arguments: "
												+ callbackType.getCallbackAnnotation().getName() + " - " + xMethod
								);
							}
							method.setAccessible( true );
							log.debugf(
									"Adding %s as %s callback for entity %s",
									methodName,
									callbackType.getCallbackAnnotation().getSimpleName(),
									beanClass.getName()
							);
							callbacks.add( 0, callback ); //superclass first
							callbacksMethodNames.add( 0, methodName );
						}
						else {
							throw new PersistenceException(
									"You can only annotate one callback method with "
											+ callbackType.getCallbackAnnotation().getName() + " in bean class: " + beanClass.getName()
							);
						}
					}
				}
			}
			if ( !stopListeners ) {
				getListeners( currentClazz, orderedListeners );
				stopListeners = currentClazz.isAnnotationPresent( ExcludeSuperclassListeners.class );
				stopDefaultListeners = currentClazz.isAnnotationPresent( ExcludeDefaultListeners.class );
			}

			do {
				currentClazz = currentClazz.getSuperclass();
			}
			while ( currentClazz != null
					&& !( currentClazz.isAnnotationPresent( Entity.class )
					|| currentClazz.isAnnotationPresent( MappedSuperclass.class ) )
					);
		}
		while ( currentClazz != null );

		//handle default listeners
		if ( !stopDefaultListeners ) {
			List defaultListeners = (List) reflectionManager.getDefaults().get( EntityListeners.class );

			if ( defaultListeners != null ) {
				int defaultListenerSize = defaultListeners.size();
				for ( int i = defaultListenerSize - 1; i >= 0; i-- ) {
					orderedListeners.add( defaultListeners.get( i ) );
				}
			}
		}

		for ( Class listener : orderedListeners ) {
			Callback callback = null;
			if ( listener != null ) {
				XClass xListener = reflectionManager.toXClass( listener );
				callbacksMethodNames = new ArrayList();
				List methods = xListener.getDeclaredMethods();
				for ( final XMethod xMethod : methods ) {
					if ( xMethod.isAnnotationPresent( callbackType.getCallbackAnnotation() ) ) {
						final Method method = reflectionManager.toMethod( xMethod );
						final String methodName = method.getName();
						if ( !callbacksMethodNames.contains( methodName ) ) {
							//overridden method, remove the superclass overridden method
							if ( callback == null ) {
								callback = new ListenerCallback(
										jpaListenerFactory.buildListener( listener ),
										method,
										callbackType
								);

								Class returnType = method.getReturnType();
								Class[] args = method.getParameterTypes();
								if ( returnType != Void.TYPE || args.length != 1 ) {
									throw new PersistenceException(
											"Callback methods annotated in a listener bean class must return void and take one argument: "
													+ callbackType.getCallbackAnnotation().getName() + " - " + method
									);
								}
								if ( !method.isAccessible() ) {
									method.setAccessible( true );
								}
								log.debugf(
										"Adding %s as %s callback for entity %s",
										methodName,
										callbackType.getCallbackAnnotation().getSimpleName(),
										beanClass.getName()
								);
								callbacks.add( 0, callback ); // listeners first
							}
							else {
								throw new PersistenceException(
										"You can only annotate one callback method with "
												+ callbackType.getCallbackAnnotation().getName()
												+ " in bean class: " + beanClass.getName()
												+ " and callback listener: " + listener.getName()
								);
							}
						}
					}
				}
			}
		}
		return callbacks.toArray( new Callback[callbacks.size()] );
	}

	private static boolean useAnnotationAnnotatedByListener;

	static {
		//check whether reading annotations of annotations is useful or not
		useAnnotationAnnotatedByListener = false;
		Target target = EntityListeners.class.getAnnotation( Target.class );
		if ( target != null ) {
			for ( ElementType type : target.value() ) {
				if ( type.equals( ElementType.ANNOTATION_TYPE ) ) {
					useAnnotationAnnotatedByListener = true;
				}
			}
		}
	}

	private static void getListeners(XClass currentClazz, List orderedListeners) {
		EntityListeners entityListeners = currentClazz.getAnnotation( EntityListeners.class );
		if ( entityListeners != null ) {
			Class[] classes = entityListeners.value();
			int size = classes.length;
			for ( int index = size - 1; index >= 0; index-- ) {
				orderedListeners.add( classes[index] );
			}
		}
		if ( useAnnotationAnnotatedByListener ) {
			Annotation[] annotations = currentClazz.getAnnotations();
			for ( Annotation annot : annotations ) {
				entityListeners = annot.getClass().getAnnotation( EntityListeners.class );
				if ( entityListeners != null ) {
					Class[] classes = entityListeners.value();
					int size = classes.length;
					for ( int index = size - 1; index >= 0; index-- ) {
						orderedListeners.add( classes[index] );
					}
				}
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy