diff --git a/hibernate-entitymanager/hibernate-entitymanager.gradle b/hibernate-entitymanager/hibernate-entitymanager.gradle index 57540cedc0..ad3011e416 100755 --- a/hibernate-entitymanager/hibernate-entitymanager.gradle +++ b/hibernate-entitymanager/hibernate-entitymanager.gradle @@ -33,6 +33,10 @@ dependencies { testCompile( libraries.validation ) testCompile( "org.jboss.weld:weld-core:2.0.0.Beta6" ) testCompile( "org.jboss.weld.arquillian.container:arquillian-weld-ee-embedded-1.1:1.1.2.Final" ) + testCompile( "javax.enterprise:cdi-api:1.1-PFD" ) { + // we need to force it to make sure we influence the one coming from arquillian + force=true + } testCompile( libraries.mockito ) testRuntime( libraries.validator ) diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/AvailableSettings.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/AvailableSettings.java index 5dfc063e6b..50480fb008 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/AvailableSettings.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/AvailableSettings.java @@ -555,4 +555,12 @@ public interface AvailableSettings { * Used to pass along the name of the persistence unit. */ String PERSISTENCE_UNIT_NAME = "hibernate.ejb.persistenceUnitName"; + + /** + * Defines delayed access to CDI BeanManager. Starting in 5.1 the preferred means for CDI + * bootstrapping is through org.hibernate.jpa.event.spi.jpa.ExtendedBeanManager + * + * @since 5.0.8 + */ + String DELAY_CDI_ACCESS = "hibernate.delay_cdi_access"; } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaDeleteEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaDeleteEventListener.java index 5c730784ad..4e062abb89 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaDeleteEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaDeleteEventListener.java @@ -11,7 +11,7 @@ import java.io.Serializable; import org.hibernate.event.internal.DefaultDeleteEventListener; import org.hibernate.event.spi.DeleteEvent; import org.hibernate.event.spi.EventSource; -import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistryConsumer; import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; import org.hibernate.persister.entity.EntityPersister; diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaFlushEntityEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaFlushEntityEventListener.java index f73c24988c..b5bf8ad9f6 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaFlushEntityEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaFlushEntityEventListener.java @@ -12,7 +12,7 @@ import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.Status; import org.hibernate.event.internal.DefaultFlushEntityEventListener; -import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistryConsumer; import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; import org.hibernate.metadata.ClassMetadata; import org.hibernate.persister.entity.EntityPersister; diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaMergeEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaMergeEventListener.java index 5693280a92..7e0c4cfaa1 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaMergeEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaMergeEventListener.java @@ -10,7 +10,7 @@ import java.io.Serializable; import org.hibernate.event.internal.DefaultMergeEventListener; import org.hibernate.event.spi.EventSource; -import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistryConsumer; import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; /** diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPersistEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPersistEventListener.java index 8d8bcfe7bc..db33d4763b 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPersistEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPersistEventListener.java @@ -15,7 +15,7 @@ import org.hibernate.engine.spi.CascadingAction; import org.hibernate.engine.spi.CascadingActions; import org.hibernate.event.internal.DefaultPersistEventListener; import org.hibernate.event.spi.EventSource; -import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistryConsumer; import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; import org.hibernate.type.CollectionType; diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostDeleteEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostDeleteEventListener.java index 8ed010ab6b..4677095619 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostDeleteEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostDeleteEventListener.java @@ -8,7 +8,7 @@ package org.hibernate.jpa.event.internal.core; import org.hibernate.event.spi.PostDeleteEvent; import org.hibernate.event.spi.PostDeleteEventListener; -import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistryConsumer; import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; import org.hibernate.persister.entity.EntityPersister; diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostInsertEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostInsertEventListener.java index 1eb1e6991b..7e01ff4da1 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostInsertEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostInsertEventListener.java @@ -8,7 +8,7 @@ package org.hibernate.jpa.event.internal.core; import org.hibernate.event.spi.PostInsertEvent; import org.hibernate.event.spi.PostInsertEventListener; -import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistryConsumer; import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; import org.hibernate.persister.entity.EntityPersister; diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostLoadEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostLoadEventListener.java index 334d9e50bd..9ab9e0e0cf 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostLoadEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostLoadEventListener.java @@ -8,7 +8,7 @@ package org.hibernate.jpa.event.internal.core; import org.hibernate.event.spi.PostLoadEvent; import org.hibernate.event.spi.PostLoadEventListener; -import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistryConsumer; import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; /** diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostUpdateEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostUpdateEventListener.java index 65b8c69b13..d1e6291826 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostUpdateEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostUpdateEventListener.java @@ -17,7 +17,7 @@ import org.hibernate.event.spi.PostCollectionUpdateEvent; import org.hibernate.event.spi.PostCollectionUpdateEventListener; import org.hibernate.event.spi.PostUpdateEvent; import org.hibernate.event.spi.PostUpdateEventListener; -import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistryConsumer; import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; import org.hibernate.persister.entity.EntityPersister; diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaSaveEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaSaveEventListener.java index b7c8d19546..d72a7d9a2a 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaSaveEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaSaveEventListener.java @@ -10,7 +10,7 @@ import java.io.Serializable; import org.hibernate.event.internal.DefaultSaveEventListener; import org.hibernate.event.spi.EventSource; -import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistryConsumer; import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; /** diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaSaveOrUpdateEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaSaveOrUpdateEventListener.java index 4d08b0acb6..73446a1805 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaSaveOrUpdateEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaSaveOrUpdateEventListener.java @@ -10,7 +10,7 @@ import java.io.Serializable; import org.hibernate.event.internal.DefaultSaveOrUpdateEventListener; import org.hibernate.event.spi.EventSource; -import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistryConsumer; import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; /** diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/AbstractCallback.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/AbstractCallback.java new file mode 100644 index 0000000000..cac0130a42 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/AbstractCallback.java @@ -0,0 +1,31 @@ +/* + * 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 org.hibernate.jpa.event.spi.jpa.Callback; +import org.hibernate.jpa.event.spi.jpa.CallbackType; + +/** + * @author Steve Ebersole + */ +public abstract class AbstractCallback implements Callback { + private final CallbackType callbackType; + + public AbstractCallback(CallbackType callbackType) { + this.callbackType = callbackType; + } + + @Override + public boolean isActive() { + return true; + } + + @Override + public CallbackType getCallbackType() { + return callbackType; + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/BeanManagerListenerFactory.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/BeanManagerListenerFactory.java deleted file mode 100644 index b046bde4f6..0000000000 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/BeanManagerListenerFactory.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import javax.enterprise.context.spi.CreationalContext; -import javax.enterprise.inject.spi.AnnotatedType; -import javax.enterprise.inject.spi.BeanManager; -import javax.enterprise.inject.spi.InjectionTarget; - -import org.hibernate.jpa.event.spi.jpa.ListenerFactory; - -/** - * CID-based implementation of the ListenerFactory contract. Listener instances are kept in a map keyed by Class - * to achieve singleton-ness. - * - * @author Steve Ebersole - */ -public class BeanManagerListenerFactory implements ListenerFactory { - private final BeanManager beanManager; - private final Map listeners = new ConcurrentHashMap(); - - public static BeanManagerListenerFactory fromBeanManagerReference(Object beanManagerReference) { - return new BeanManagerListenerFactory( ( BeanManager ) beanManagerReference ); - } - - public BeanManagerListenerFactory(BeanManager beanManager) { - this.beanManager = beanManager; - } - - @Override - public T buildListener(Class listenerClass) { - BeanMetaData beanMetaData = listeners.get( listenerClass ); - if ( beanMetaData == null ) { - beanMetaData = new BeanMetaData( listenerClass ); - listeners.put( listenerClass, beanMetaData ); - } - return beanMetaData.instance; - } - - @Override - public void release() { - for ( BeanMetaData beanMetaData : listeners.values() ) { - beanMetaData.release(); - } - listeners.clear(); - } - - private class BeanMetaData { - private final InjectionTarget injectionTarget; - private final CreationalContext creationalContext; - private final T instance; - - private BeanMetaData(Class listenerClass) { - AnnotatedType annotatedType = beanManager.createAnnotatedType( listenerClass ); - this.injectionTarget = beanManager.createInjectionTarget( annotatedType ); - this.creationalContext = beanManager.createCreationalContext( null ); - - this.instance = injectionTarget.produce( creationalContext ); - injectionTarget.inject( this.instance, creationalContext ); - - injectionTarget.postConstruct( this.instance ); - } - - private void release() { - injectionTarget.preDestroy( instance ); - injectionTarget.dispose( instance ); - creationalContext.release(); - } - } -} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/LegacyCallbackProcessor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackBuilderLegacyImpl.java similarity index 78% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/LegacyCallbackProcessor.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackBuilderLegacyImpl.java index f7d0c9da72..fe3a8007c2 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/LegacyCallbackProcessor.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackBuilderLegacyImpl.java @@ -25,33 +25,36 @@ 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; /** - * @author Kabir Khan + * EntityCallbackBuilder implementation using HCANN ReflectionManager. "legacy" in that + * we want to move to Jandex instead. + * * @author Steve Ebersole */ -public class LegacyCallbackProcessor implements CallbackProcessor { - private static final Logger log = Logger.getLogger( LegacyCallbackProcessor.class ); +public class CallbackBuilderLegacyImpl implements CallbackBuilder { + private static final Logger log = Logger.getLogger( CallbackBuilderLegacyImpl.class ); private final ListenerFactory jpaListenerFactory; private final ReflectionManager reflectionManager; - public LegacyCallbackProcessor(ListenerFactory jpaListenerFactory, ReflectionManager reflectionManager) { + public CallbackBuilderLegacyImpl(ListenerFactory jpaListenerFactory, ReflectionManager reflectionManager) { this.jpaListenerFactory = jpaListenerFactory; this.reflectionManager = reflectionManager; } @Override - public void processCallbacksForEntity(Object entityObject, CallbackRegistryImpl callbackRegistry) { - final String entityClassName = (String) entityObject; + public void buildCallbacksForEntity(String entityClassName, CallbackRegistrar callbackRegistrar) { try { final XClass entityXClass = reflectionManager.classForName( entityClassName ); final Class entityClass = reflectionManager.toClass( entityXClass ); - for ( Class annotationClass : CALLBACK_ANNOTATION_CLASSES ) { - if ( callbackRegistry.hasRegisteredCallbacks( entityClass, annotationClass ) ) { + 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( @@ -59,12 +62,12 @@ public class LegacyCallbackProcessor implements CallbackProcessor { "assuming this means the class was mapped twice " + "(using hbm.xml entity-name support) - skipping subsequent registrations", entityClassName, - annotationClass.getSimpleName() + callbackType.getCallbackAnnotation().getSimpleName() ); continue; } - final Callback[] callbacks = resolveCallbacks( entityXClass, annotationClass, reflectionManager ); - callbackRegistry.addEntityCallbacks( entityClass, annotationClass, callbacks ); + final Callback[] callbacks = resolveCallbacks( entityXClass, callbackType, reflectionManager ); + callbackRegistrar.registerCallbacks( entityClass, callbacks ); } } catch (ClassLoadingException e) { @@ -72,7 +75,12 @@ public class LegacyCallbackProcessor implements CallbackProcessor { } } - public Callback[] resolveCallbacks(XClass beanClass, Class annotation, ReflectionManager reflectionManager) { + @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(); @@ -83,26 +91,26 @@ public class LegacyCallbackProcessor implements CallbackProcessor { Callback callback = null; List methods = currentClazz.getDeclaredMethods(); for ( final XMethod xMethod : methods ) { - if ( xMethod.isAnnotationPresent( annotation ) ) { + 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 ); + 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: " + annotation - .getName() + " - " + xMethod + "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, - annotation.getSimpleName(), + callbackType.getCallbackAnnotation().getSimpleName(), beanClass.getName() ); callbacks.add( 0, callback ); //superclass first @@ -111,7 +119,7 @@ public class LegacyCallbackProcessor implements CallbackProcessor { else { throw new PersistenceException( "You can only annotate one callback method with " - + annotation.getName() + " in bean class: " + beanClass.getName() + + callbackType.getCallbackAnnotation().getName() + " in bean class: " + beanClass.getName() ); } } @@ -152,20 +160,24 @@ public class LegacyCallbackProcessor implements CallbackProcessor { callbacksMethodNames = new ArrayList(); List methods = xListener.getDeclaredMethods(); for ( final XMethod xMethod : methods ) { - if ( xMethod.isAnnotationPresent( annotation ) ) { + 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 ); + 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: " + annotation - .getName() + " - " + method + "Callback methods annotated in a listener bean class must return void and take one argument: " + + callbackType.getCallbackAnnotation().getName() + " - " + method ); } if ( !method.isAccessible() ) { @@ -174,7 +186,7 @@ public class LegacyCallbackProcessor implements CallbackProcessor { log.debugf( "Adding %s as %s callback for entity %s", methodName, - annotation.getSimpleName(), + callbackType.getCallbackAnnotation().getSimpleName(), beanClass.getName() ); callbacks.add( 0, callback ); // listeners first @@ -182,8 +194,9 @@ public class LegacyCallbackProcessor implements CallbackProcessor { else { throw new PersistenceException( "You can only annotate one callback method with " - + annotation.getName() + " in bean class: " + beanClass.getName() + " and callback listener: " - + listener.getName() + + callbackType.getCallbackAnnotation().getName() + + " in bean class: " + beanClass.getName() + + " and callback listener: " + listener.getName() ); } } @@ -232,9 +245,4 @@ public class LegacyCallbackProcessor implements CallbackProcessor { } } } - - @Override - public void release() { - // nothing to do here - } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackProcessor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackProcessor.java deleted file mode 100644 index 50c2f8a2de..0000000000 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackProcessor.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 javax.persistence.PostLoad; -import javax.persistence.PostPersist; -import javax.persistence.PostRemove; -import javax.persistence.PostUpdate; -import javax.persistence.PrePersist; -import javax.persistence.PreRemove; -import javax.persistence.PreUpdate; - -/** - * Delegate for interpreting, parsing and processing callbacks - * - * @author Steve Ebersole - */ -public interface CallbackProcessor { - public static final Class[] CALLBACK_ANNOTATION_CLASSES = new Class[] { - PreUpdate.class, PostUpdate.class, - PrePersist.class, PostPersist.class, - PreRemove.class, PostRemove.class, - PostLoad.class - }; - - /** - * Ugh, Object to account for Configuration/Metamodel split. Should eventually be EntityBinding from - * metamodel code base. Currently each Integrator method passes in different type and each impl - * interprets differently. - * - * @param entityObject - * @param registry - */ - public void processCallbacksForEntity(Object entityObject, CallbackRegistryImpl registry); - - public void release(); -} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryImpl.java index 94ba717696..4d12117c75 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryImpl.java @@ -8,16 +8,11 @@ package org.hibernate.jpa.event.internal.jpa; import java.util.HashMap; import javax.persistence.PersistenceException; -import javax.persistence.PostLoad; -import javax.persistence.PostPersist; -import javax.persistence.PostRemove; -import javax.persistence.PostUpdate; -import javax.persistence.PrePersist; -import javax.persistence.PreRemove; -import javax.persistence.PreUpdate; import org.hibernate.jpa.event.spi.jpa.Callback; import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; +import org.hibernate.jpa.event.spi.jpa.CallbackType; +import org.hibernate.jpa.event.spi.jpa.CallbackBuilder; /** * Keep track of all lifecycle callbacks and listeners for a given persistence unit @@ -26,7 +21,7 @@ import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; * @author Steve Ebersole */ @SuppressWarnings({"unchecked", "serial"}) -public class CallbackRegistryImpl implements CallbackRegistry { +public class CallbackRegistryImpl implements CallbackRegistry, CallbackBuilder.CallbackRegistrar { private HashMap preCreates = new HashMap(); private HashMap postCreates = new HashMap(); private HashMap preRemoves = new HashMap(); @@ -36,13 +31,27 @@ public class CallbackRegistryImpl implements CallbackRegistry { private HashMap postLoads = new HashMap(); @Override - public void preCreate(Object bean) { - callback( preCreates.get( bean.getClass() ), bean ); + public boolean hasRegisteredCallbacks(Class entityClass, CallbackType callbackType) { + final HashMap map = determineAppropriateCallbackMap( callbackType ); + return notEmpty( map.get( entityClass ) ); } @Override - public boolean hasPostCreateCallbacks(Class entityClass) { - return notEmpty( preCreates.get( entityClass ) ); + public void registerCallbacks(Class entityClass, Callback[] callbacks) { + if ( callbacks == null || callbacks.length == 0 ) { + return; + } + + final HashMap map = determineAppropriateCallbackMap( callbacks[0].getCallbackType() ); + if ( map.containsKey( entityClass ) ) { + throw new PersistenceException( "Error build callback listeners; entity [" + entityClass.getName() + " was already processed" ); + } + map.put( entityClass, callbacks ); + } + + @Override + public void preCreate(Object bean) { + callback( preCreates.get( bean.getClass() ), bean ); } private boolean notEmpty(Callback[] callbacks) { @@ -59,11 +68,6 @@ public class CallbackRegistryImpl implements CallbackRegistry { return callback( preUpdates.get( bean.getClass() ), bean ); } - @Override - public boolean hasPostUpdateCallbacks(Class entityClass) { - return notEmpty( postUpdates.get( entityClass ) ); - } - @Override public void postUpdate(Object bean) { callback( postUpdates.get( bean.getClass() ), bean ); @@ -74,11 +78,6 @@ public class CallbackRegistryImpl implements CallbackRegistry { callback( preRemoves.get( bean.getClass() ), bean ); } - @Override - public boolean hasPostRemoveCallbacks(Class entityClass) { - return notEmpty( postRemoves.get( entityClass ) ); - } - @Override public void postRemove(Object bean) { callback( postRemoves.get( bean.getClass() ), bean ); @@ -101,56 +100,36 @@ public class CallbackRegistryImpl implements CallbackRegistry { } } - @Override - public boolean hasRegisteredCallbacks(Class entityClass, Class annotationClass) { - final HashMap map = determineAppropriateCallbackMap( annotationClass ); - return map != null && map.containsKey( entityClass ); - } - - /** - * Great care should be taken calling this. Not a fan of it being public, but that is needed because of - * @param entityClass - * @param annotationClass - * @param callbacks - */ - public void addEntityCallbacks(Class entityClass, Class annotationClass, Callback[] callbacks) { - final HashMap map = determineAppropriateCallbackMap( annotationClass ); - if ( map.containsKey( entityClass ) ) { - throw new PersistenceException( "Error build callback listeners; entity [" + entityClass.getName() + " was already processed" ); - } - map.put( entityClass, callbacks ); - } - - private HashMap determineAppropriateCallbackMap(Class annotationClass) { - if ( PrePersist.class.equals( annotationClass ) ) { + private HashMap determineAppropriateCallbackMap(CallbackType callbackType) { + if ( callbackType == CallbackType.PRE_PERSIST ) { return preCreates; } - if ( PostPersist.class.equals( annotationClass ) ) { + if ( callbackType == CallbackType.POST_PERSIST ) { return postCreates; } - if ( PreRemove.class.equals( annotationClass ) ) { + if ( callbackType == CallbackType.PRE_REMOVE ) { return preRemoves; } - if ( PostRemove.class.equals( annotationClass ) ) { + if ( callbackType == CallbackType.POST_REMOVE ) { return postRemoves; } - if ( PreUpdate.class.equals( annotationClass ) ) { + if ( callbackType == CallbackType.PRE_UPDATE ) { return preUpdates; } - if ( PostUpdate.class.equals( annotationClass ) ) { + if ( callbackType == CallbackType.POST_UPDATE ) { return postUpdates; } - if ( PostLoad.class.equals( annotationClass ) ) { + if ( callbackType == CallbackType.POST_LOAD ) { return postLoads; } - throw new PersistenceException( "Unrecognized JPA callback annotation [" + annotationClass.getName() + "]" ); + throw new PersistenceException( "Unrecognized JPA callback type [" + callbackType + "]" ); } public void release() { @@ -165,4 +144,54 @@ public class CallbackRegistryImpl implements CallbackRegistry { postLoads.clear(); } + + + // deprecations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Override + public boolean hasPostCreateCallbacks(Class entityClass) { + return notEmpty( preCreates.get( entityClass ) ); + } + + @Override + public boolean hasPostUpdateCallbacks(Class entityClass) { + return notEmpty( postUpdates.get( entityClass ) ); + } + + @Override + public boolean hasPostRemoveCallbacks(Class entityClass) { + return notEmpty( postRemoves.get( entityClass ) ); + } + + @Override + public boolean hasRegisteredCallbacks(Class entityClass, Class annotationClass) { + final HashMap map = determineAppropriateCallbackMap( toCallbackType( annotationClass ) ); + return map != null && map.containsKey( entityClass ); + } + + private CallbackType toCallbackType(Class annotationClass) { + if ( annotationClass == CallbackType.POST_LOAD.getCallbackAnnotation() ) { + return CallbackType.POST_LOAD; + } + else if ( annotationClass == CallbackType.PRE_PERSIST.getCallbackAnnotation() ) { + return CallbackType.PRE_PERSIST; + } + else if ( annotationClass == CallbackType.POST_PERSIST.getCallbackAnnotation() ) { + return CallbackType.POST_PERSIST; + } + else if ( annotationClass == CallbackType.PRE_UPDATE.getCallbackAnnotation() ) { + return CallbackType.PRE_UPDATE; + } + else if ( annotationClass == CallbackType.POST_UPDATE.getCallbackAnnotation() ) { + return CallbackType.POST_UPDATE; + } + else if ( annotationClass == CallbackType.PRE_REMOVE.getCallbackAnnotation() ) { + return CallbackType.PRE_REMOVE; + } + else if ( annotationClass == CallbackType.POST_REMOVE.getCallbackAnnotation() ) { + return CallbackType.POST_REMOVE; + } + + throw new PersistenceException( "Unrecognized JPA callback annotation [" + annotationClass + "]" ); + } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/EntityCallback.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/EntityCallback.java index 56b389ab26..8e72c9543d 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/EntityCallback.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/EntityCallback.java @@ -10,6 +10,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.hibernate.jpa.event.spi.jpa.Callback; +import org.hibernate.jpa.event.spi.jpa.CallbackType; /** * Represents a JPA callback on the entity itself @@ -17,10 +18,11 @@ import org.hibernate.jpa.event.spi.jpa.Callback; * @author Kabir Khan * @author Steve Ebersole */ -public class EntityCallback implements Callback { - private Method callbackMethod; +public class EntityCallback extends AbstractCallback implements Callback { + private final Method callbackMethod; - public EntityCallback(Method callbackMethod) { + public EntityCallback(Method callbackMethod, CallbackType callbackType) { + super( callbackType ); this.callbackMethod = callbackMethod; } @@ -43,9 +45,4 @@ public class EntityCallback implements Callback { throw new RuntimeException( e ); } } - - @Override - public boolean isActive() { - return true; - } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerCallback.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerCallback.java index 7abcaf5b7b..946eda3efa 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerCallback.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerCallback.java @@ -10,6 +10,8 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.hibernate.jpa.event.spi.jpa.Callback; +import org.hibernate.jpa.event.spi.jpa.CallbackType; +import org.hibernate.jpa.event.spi.jpa.Listener; /** * Represents a JPA callback using a dedicated listener @@ -17,11 +19,12 @@ import org.hibernate.jpa.event.spi.jpa.Callback; * @author Kabir Khan * @author Steve Ebersole */ -public class ListenerCallback implements Callback { +public class ListenerCallback extends AbstractCallback implements Callback { private final Method callbackMethod; - private final Object listenerInstance; + private final Listener listenerInstance; - public ListenerCallback(Object listenerInstance, Method callbackMethod) { + public ListenerCallback(Listener listenerInstance, Method callbackMethod, CallbackType callbackType) { + super( callbackType ); this.listenerInstance = listenerInstance; this.callbackMethod = callbackMethod; } @@ -29,7 +32,7 @@ public class ListenerCallback implements Callback { @Override public boolean performCallback(Object entity) { try { - callbackMethod.invoke( listenerInstance, entity ); + callbackMethod.invoke( listenerInstance.getListener(), entity ); return true; } catch (InvocationTargetException e) { @@ -45,9 +48,4 @@ public class ListenerCallback implements Callback { throw new RuntimeException( e ); } } - - @Override - public boolean isActive() { - return true; - } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerFactoryBeanManagerDelayedImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerFactoryBeanManagerDelayedImpl.java new file mode 100644 index 0000000000..7f0d4074d0 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerFactoryBeanManagerDelayedImpl.java @@ -0,0 +1,122 @@ +/* + * 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.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.InjectionTarget; + +import org.hibernate.jpa.event.spi.jpa.Listener; +import org.hibernate.jpa.event.spi.jpa.ListenerFactory; + +import org.jboss.logging.Logger; + +/** + * CDI-based implementation of the ListenerFactory contract. This implementation + * delayes access to the CDI BeanManager until first need. + *

+ * This class appears unused, but that is because it is only referenced via reflection from + * {@link org.hibernate.jpa.event.spi.jpa.ListenerFactoryBuilder}. + * + * @author Steve Ebersole + */ +@SuppressWarnings("unused") +public class ListenerFactoryBeanManagerDelayedImpl implements ListenerFactory { + private static final Logger log = Logger.getLogger( ListenerFactoryBeanManagerDelayedImpl.class ); + + private final BeanManager beanManager; + private final Map listenerMap = new ConcurrentHashMap(); + + /** + * Used via reflection from ListenerFactoryBuilder, the intent being to isolate CDI dependency + * to just this class and its delegates in the case that a BeanManager is passed. + * + * @param reference The BeanManager reference + * + * @return A instantiated ListenerFactoryBeanManagerImpl + */ + public static ListenerFactoryBeanManagerDelayedImpl fromBeanManagerReference(Object reference) { + return new ListenerFactoryBeanManagerDelayedImpl( (BeanManager) reference ); + } + + public ListenerFactoryBeanManagerDelayedImpl(BeanManager beanManager) { + this.beanManager = beanManager; + log.debugf( "Delayed access requested to CDI BeanManager : " + beanManager ); + } + + @Override + @SuppressWarnings("unchecked") + public Listener buildListener(Class listenerClass) { + ListenerImpl listenerImpl = listenerMap.get( listenerClass ); + if ( listenerImpl == null ) { + listenerImpl = new ListenerImpl( listenerClass ); + listenerMap.put( listenerClass, listenerImpl ); + } + return (Listener) listenerImpl; + } + + @Override + public void release() { + for ( ListenerImpl listenerImpl : listenerMap.values() ) { + listenerImpl.release(); + } + listenerMap.clear(); + } + + private class ListenerImpl implements Listener { + private final Class listenerClass; + + private boolean initialized = false; + + private InjectionTarget injectionTarget; + private CreationalContext creationalContext; + private T listenerInstance; + + private ListenerImpl(Class listenerClass) { + this.listenerClass = listenerClass; + log.debugf( "Delayed CDI listener built : " + listenerClass ); + } + + public void initialize() { + log.debug( "Initializing delayed CDI listener on first use : " + listenerClass ); + AnnotatedType annotatedType = beanManager.createAnnotatedType( listenerClass ); + this.injectionTarget = beanManager.createInjectionTarget( annotatedType ); + this.creationalContext = beanManager.createCreationalContext( null ); + + this.listenerInstance = injectionTarget.produce( creationalContext ); + injectionTarget.inject( this.listenerInstance, creationalContext ); + + injectionTarget.postConstruct( this.listenerInstance ); + + this.initialized = true; + } + + @Override + public T getListener() { + if ( !initialized ) { + initialize(); + } + return listenerInstance; + } + + public void release() { + if ( !initialized ) { + log.debug( "Skipping release for CDI listener [" + listenerClass + "] as it was not initialized" ); + return; + } + + log.debug( "Releasing CDI listener : " + listenerClass ); + + injectionTarget.preDestroy( listenerInstance ); + injectionTarget.dispose( listenerInstance ); + creationalContext.release(); + } + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerFactoryBeanManagerStandardImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerFactoryBeanManagerStandardImpl.java new file mode 100644 index 0000000000..8efb97b343 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerFactoryBeanManagerStandardImpl.java @@ -0,0 +1,100 @@ +/* + * 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.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.InjectionTarget; + +import org.hibernate.jpa.event.spi.jpa.Listener; +import org.hibernate.jpa.event.spi.jpa.ListenerFactory; + +import org.jboss.logging.Logger; + +/** + * CDI-based implementation of the ListenerFactory contract. This implementation works in + * the JPA standard prescribed manner. + *

+ * This class appears unused, but that is because it is only referenced via reflection from + * {@link org.hibernate.jpa.event.spi.jpa.ListenerFactoryBuilder}. + * + * @author Steve Ebersole + */ +@SuppressWarnings("unused") +public class ListenerFactoryBeanManagerStandardImpl implements ListenerFactory { + private static final Logger log = Logger.getLogger( ListenerFactoryBeanManagerStandardImpl.class ); + + private final BeanManager beanManager; + private final Map listenerMap = new ConcurrentHashMap(); + + /** + * Used via reflection from ListenerFactoryBuilder, the intent being to isolate CDI dependency + * to just this class and its delegates in the case that a BeanManager is passed. + * + * @param reference The BeanManager reference + * + * @return A instantiated ListenerFactoryBeanManagerImpl + */ + public static ListenerFactoryBeanManagerStandardImpl fromBeanManagerReference(Object reference) { + return new ListenerFactoryBeanManagerStandardImpl( (BeanManager) reference ); + } + + public ListenerFactoryBeanManagerStandardImpl(BeanManager beanManager) { + this.beanManager = beanManager; + log.debugf( "Standard access requested to CDI BeanManager : " + beanManager ); + } + + @Override + @SuppressWarnings("unchecked") + public Listener buildListener(Class listenerClass) { + ListenerImpl listenerImpl = listenerMap.get( listenerClass ); + if ( listenerImpl == null ) { + listenerImpl = new ListenerImpl( listenerClass ); + listenerMap.put( listenerClass, listenerImpl ); + } + return (Listener) listenerImpl; + } + + @Override + public void release() { + for ( ListenerImpl listenerImpl : listenerMap.values() ) { + listenerImpl.release(); + } + listenerMap.clear(); + } + + private class ListenerImpl implements Listener { + private final InjectionTarget injectionTarget; + private final CreationalContext creationalContext; + private final T listenerInstance; + + private ListenerImpl(Class listenerClass) { + AnnotatedType annotatedType = beanManager.createAnnotatedType( listenerClass ); + this.injectionTarget = beanManager.createInjectionTarget( annotatedType ); + this.creationalContext = beanManager.createCreationalContext( null ); + + this.listenerInstance = injectionTarget.produce( creationalContext ); + injectionTarget.inject( this.listenerInstance, creationalContext ); + + injectionTarget.postConstruct( this.listenerInstance ); + } + + @Override + public T getListener() { + return listenerInstance; + } + + public void release() { + injectionTarget.preDestroy( listenerInstance ); + injectionTarget.dispose( listenerInstance ); + creationalContext.release(); + } + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerFactoryStandardImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerFactoryStandardImpl.java new file mode 100644 index 0000000000..082286e3e0 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerFactoryStandardImpl.java @@ -0,0 +1,66 @@ +/* + * 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.util.concurrent.ConcurrentHashMap; +import javax.persistence.PersistenceException; + +import org.hibernate.jpa.event.spi.jpa.Listener; +import org.hibernate.jpa.event.spi.jpa.ListenerFactory; + +/** + * Standard implementation of the ListenerFactory contract using simple instantiation. + * + * @author Steve Ebersole + */ +public class ListenerFactoryStandardImpl implements ListenerFactory { + private final ConcurrentHashMap listenerInstances = new ConcurrentHashMap(); + + @Override + @SuppressWarnings("unchecked") + public Listener buildListener(Class listenerClass) { + ListenerImpl listenerImpl = listenerInstances.get( listenerClass ); + if ( listenerImpl == null ) { + try { + T listenerInstance = listenerClass.newInstance(); + listenerImpl = new ListenerImpl( listenerInstance ); + } + catch (Exception e) { + throw new PersistenceException( + "Unable to create instance of " + listenerClass.getName() + " as a JPA callback listener", + e + ); + } + ListenerImpl existing = listenerInstances.putIfAbsent( + listenerClass, + listenerImpl + ); + if ( existing != null ) { + listenerImpl = existing; + } + } + return (Listener) listenerImpl; + } + + @Override + public void release() { + listenerInstances.clear(); + } + + private static class ListenerImpl implements Listener { + private final T listenerInstance; + + public ListenerImpl(T listenerInstance) { + this.listenerInstance = listenerInstance; + } + + @Override + public T getListener() { + return listenerInstance; + } + } +} \ No newline at end of file diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/StandardListenerFactory.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/StandardListenerFactory.java deleted file mode 100644 index 85a2cd1839..0000000000 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/StandardListenerFactory.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.util.concurrent.ConcurrentHashMap; -import javax.persistence.PersistenceException; - -import org.hibernate.jpa.event.spi.jpa.ListenerFactory; - -/** - * Standard implementation of the ListenerFactory contract using simple instantiation. Listener instances - * are kept in a map keyed by Class to achieve singleton-ness. - * - * @author Steve Ebersole - */ -public class StandardListenerFactory implements ListenerFactory { - - private final ConcurrentHashMap listenerInstances = new ConcurrentHashMap(); - - @Override - @SuppressWarnings("unchecked") - public T buildListener(Class listenerClass) { - Object listenerInstance = listenerInstances.get( listenerClass ); - if ( listenerInstance == null ) { - try { - listenerInstance = listenerClass.newInstance(); - } - catch (Exception e) { - throw new PersistenceException( - "Unable to create instance of " + listenerClass.getName() + " as a JPA callback listener", - e - ); - } - Object existing = listenerInstances.putIfAbsent( listenerClass, listenerInstance ); - if ( existing != null ) { - listenerInstance = existing; - } - } - return (T) listenerInstance; - } - - @Override - public void release() { - listenerInstances.clear(); - } -} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java index 49a766b168..3edeca6d15 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java @@ -40,12 +40,12 @@ import org.hibernate.jpa.event.internal.core.JpaPostLoadEventListener; import org.hibernate.jpa.event.internal.core.JpaPostUpdateEventListener; import org.hibernate.jpa.event.internal.core.JpaSaveEventListener; import org.hibernate.jpa.event.internal.core.JpaSaveOrUpdateEventListener; -import org.hibernate.jpa.event.internal.jpa.CallbackProcessor; -import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; import org.hibernate.jpa.event.internal.jpa.CallbackRegistryImpl; -import org.hibernate.jpa.event.internal.jpa.LegacyCallbackProcessor; -import org.hibernate.jpa.event.internal.jpa.StandardListenerFactory; +import org.hibernate.jpa.event.internal.jpa.CallbackBuilderLegacyImpl; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackBuilder; import org.hibernate.jpa.event.spi.jpa.ListenerFactory; +import org.hibernate.jpa.event.spi.jpa.ListenerFactoryBuilder; import org.hibernate.mapping.PersistentClass; import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.service.spi.SessionFactoryServiceRegistry; @@ -57,7 +57,7 @@ import org.hibernate.service.spi.SessionFactoryServiceRegistry; */ public class JpaIntegrator implements Integrator { private ListenerFactory jpaListenerFactory; - private CallbackProcessor callbackProcessor; + private CallbackBuilder callbackBuilder; private CallbackRegistryImpl callbackRegistry; private static final DuplicationStrategy JPA_DUPLICATION_STRATEGY = new JPADuplicationStrategy(); @@ -126,18 +126,14 @@ public class JpaIntegrator implements Integrator { .getReflectionManager(); this.callbackRegistry = new CallbackRegistryImpl(); - final Object beanManagerRef = sessionFactory.getSessionFactoryOptions().getBeanManagerReference(); - this.jpaListenerFactory = beanManagerRef == null - ? new StandardListenerFactory() - : buildBeanManagerListenerFactory( beanManagerRef ); - this.callbackProcessor = new LegacyCallbackProcessor( jpaListenerFactory, reflectionManager ); - + this.jpaListenerFactory = ListenerFactoryBuilder.buildListenerFactory( sessionFactory.getSessionFactoryOptions() ); + this.callbackBuilder = new CallbackBuilderLegacyImpl( jpaListenerFactory, reflectionManager ); for ( PersistentClass persistentClass : metadata.getEntityBindings() ) { if ( persistentClass.getClassName() == null ) { // we can have non java class persisted by hibernate continue; } - callbackProcessor.processCallbacksForEntity( persistentClass.getClassName(), callbackRegistry ); + callbackBuilder.buildCallbacksForEntity( persistentClass.getClassName(), callbackRegistry ); } for ( EventType eventType : EventType.values() ) { @@ -191,8 +187,8 @@ public class JpaIntegrator implements Integrator { if ( callbackRegistry != null ) { callbackRegistry.release(); } - if ( callbackProcessor != null ) { - callbackProcessor.release(); + if ( callbackBuilder != null ) { + callbackBuilder.release(); } if ( jpaListenerFactory != null ) { jpaListenerFactory.release(); diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/Callback.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/Callback.java index ea30797c94..98b9ded213 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/Callback.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/Callback.java @@ -9,12 +9,29 @@ package org.hibernate.jpa.event.spi.jpa; import java.io.Serializable; /** - * Represents a JPA event callback. + * Represents a JPA event callback (the method). + *

+ * Generally there are 2 flavors of this; either an annotated method on the entity itself + * or an annotated method on a separate "listener" class. This contract unifies both of + * these cases. * * @author Kabir Khan * @author Steve Ebersole */ public interface Callback extends Serializable { + /** + * Is this callback active (will it do anything)? + * + * @return {@code true} if the callback is active, {@code false} otherwise. + * + * @deprecated I can actually find no usages of this method and have no idea + * why it is here :) + */ + @Deprecated + boolean isActive(); + + CallbackType getCallbackType(); + /** * Contract for performing the callback * @@ -22,12 +39,5 @@ public interface Callback extends Serializable { * * @return Did a callback actually happen? */ - public boolean performCallback(Object entity); - - /** - * Is this callback active (will it do anything)? - * - * @return - */ - public boolean isActive(); + boolean performCallback(Object entity); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackBuilder.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackBuilder.java new file mode 100644 index 0000000000..fb5a14402b --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackBuilder.java @@ -0,0 +1,32 @@ +/* + * 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.spi.jpa; + +/** + * Contract for walking an entity hierarchy and building a list of JPA callbacks + * + * @author Steve Ebersole + */ +public interface CallbackBuilder { + /** + * Represents the target of JPA callback registrations as part the EntityCallbackBuilder + */ + interface CallbackRegistrar extends CallbackRegistry { + + /** + * Register the callback against the given entity. + * + * @param entityClass The entity Class to register the Callbacks against + * @param callbacks The Callbacks to register against the given entity Class + */ + void registerCallbacks(Class entityClass, Callback[] callbacks); + } + + void buildCallbacksForEntity(String entityName, CallbackRegistrar callbackRegistrar); + + void release(); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackRegistry.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackRegistry.java index 6a8f8d3f0c..c0581bee82 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackRegistry.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackRegistry.java @@ -12,19 +12,52 @@ import java.io.Serializable; * @author Steve Ebersole */ public interface CallbackRegistry extends Serializable { + /** + * Do we have any registered callbacks of the given type for the given entity? + * + * @param entityClass The entity Class to check against + * @param callbackType The type of callback to look for + * + * @return {@code true} indicates there are already registered callbacks of + * that type for that class; {@code false} indicates there are not. + */ + boolean hasRegisteredCallbacks(Class entityClass, CallbackType callbackType); + void preCreate(Object entity); - boolean hasPostCreateCallbacks(Class entityClass); void postCreate(Object entity); boolean preUpdate(Object entity); - boolean hasPostUpdateCallbacks(Class entityClass); void postUpdate(Object entity); void preRemove(Object entity); - boolean hasPostRemoveCallbacks(Class entityClass); void postRemove(Object entity); boolean postLoad(Object entity); + /** + * @deprecated Use {@link #hasRegisteredCallbacks(Class, CallbackType)} instead passing + * {@link CallbackType#POST_PERSIST} + */ + @Deprecated + boolean hasPostCreateCallbacks(Class entityClass); + + /** + * @deprecated Use {@link #hasRegisteredCallbacks(Class, CallbackType)} instead passing + * {@link CallbackType#POST_UPDATE} + */ + @Deprecated + boolean hasPostUpdateCallbacks(Class entityClass); + + /** + * @deprecated Use {@link #hasRegisteredCallbacks(Class, CallbackType)} instead passing + * {@link CallbackType#POST_REMOVE} + */ + @Deprecated + boolean hasPostRemoveCallbacks(Class entityClass); + + /** + * @deprecated Use {@link #hasRegisteredCallbacks(Class, CallbackType)} instead. + */ + @Deprecated boolean hasRegisteredCallbacks(Class entityClass, Class annotationClass); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryConsumer.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackRegistryConsumer.java similarity index 78% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryConsumer.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackRegistryConsumer.java index d50ace66dc..4a56666024 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryConsumer.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackRegistryConsumer.java @@ -4,10 +4,9 @@ * 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; +package org.hibernate.jpa.event.spi.jpa; import org.hibernate.jpa.event.internal.core.HibernateEntityManagerEventListener; -import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; /** * Contract for injecting the registry of Callbacks into event listeners. @@ -21,5 +20,5 @@ public interface CallbackRegistryConsumer extends HibernateEntityManagerEventLis * * @param callbackRegistry The CallbackRegistry */ - public void injectCallbackRegistry(CallbackRegistry callbackRegistry); + void injectCallbackRegistry(CallbackRegistry callbackRegistry); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackType.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackType.java new file mode 100644 index 0000000000..34ec1a1f70 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackType.java @@ -0,0 +1,40 @@ +/* + * 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.spi.jpa; + +import java.lang.annotation.Annotation; +import javax.persistence.PostLoad; +import javax.persistence.PostPersist; +import javax.persistence.PostRemove; +import javax.persistence.PostUpdate; +import javax.persistence.PrePersist; +import javax.persistence.PreRemove; +import javax.persistence.PreUpdate; + +/** + * @author Steve Ebersole + */ +public enum CallbackType { + PRE_UPDATE( PreUpdate.class ), + POST_UPDATE( PostUpdate.class ), + PRE_PERSIST( PrePersist.class ), + POST_PERSIST( PostPersist.class ), + PRE_REMOVE( PreRemove.class ), + POST_REMOVE( PostRemove.class ), + POST_LOAD( PostLoad.class ) + ; + + private Class callbackAnnotation; + + CallbackType(Class callbackAnnotation) { + this.callbackAnnotation = callbackAnnotation; + } + + public Class getCallbackAnnotation() { + return callbackAnnotation; + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/Listener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/Listener.java new file mode 100644 index 0000000000..29ba58b01e --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/Listener.java @@ -0,0 +1,17 @@ +/* + * 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.spi.jpa; + +/** + * Encapsulates access to the listener instance for listener callbacks + * ({@link javax.persistence.EntityListeners}). + * + * @author Steve Ebersole + */ +public interface Listener { + T getListener(); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/ListenerFactory.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/ListenerFactory.java index e27c536ef4..a4be7e77db 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/ListenerFactory.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/ListenerFactory.java @@ -7,12 +7,16 @@ package org.hibernate.jpa.event.spi.jpa; /** - * Factory for building instances user-specified event listeners. + * Contract for building instances of JPA callback listener classes. + *

+ * Listener instances should be uniqued by Class such that the same instance is + * returned for any calls to {@link #buildListener} with the same class. + * + * @see javax.persistence.EntityListeners * * @author Steve Ebersole */ public interface ListenerFactory { - public T buildListener(Class listenerClass); - - public void release(); + Listener buildListener(Class listenerClass); + void release(); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/ListenerFactoryBuilder.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/ListenerFactoryBuilder.java new file mode 100644 index 0000000000..641e5f9b84 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/ListenerFactoryBuilder.java @@ -0,0 +1,95 @@ +/* + * 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.spi.jpa; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.hibernate.HibernateException; +import org.hibernate.boot.spi.SessionFactoryOptions; +import org.hibernate.engine.config.spi.ConfigurationService; +import org.hibernate.engine.config.spi.StandardConverters; +import org.hibernate.jpa.AvailableSettings; +import org.hibernate.jpa.event.internal.jpa.ListenerFactoryStandardImpl; + +/** + * Builder for ListenerFactory based on configuration options + * + * @author Steve Ebersole + */ +public class ListenerFactoryBuilder { + + public static ListenerFactory buildListenerFactory(SessionFactoryOptions options) { + final Object beanManagerRef = options.getBeanManagerReference(); + if ( beanManagerRef == null ) { + return new ListenerFactoryStandardImpl(); + } + else { + final boolean delayAccessToCdi = options.getServiceRegistry() + .getService( ConfigurationService.class ) + .getSetting( AvailableSettings.DELAY_CDI_ACCESS, StandardConverters.BOOLEAN, false ); + if ( delayAccessToCdi ) { + return buildDelayedBeanManagerListenerFactory( beanManagerRef ); + } + else { + return buildStandardBeanManagerListenerFactory( beanManagerRef ); + } + } + } + + + private static final String CDI_LISTENER_FACTORY_STANDARD_CLASS = "org.hibernate.jpa.event.internal.jpa.ListenerFactoryBeanManagerStandardImpl"; + private static final String CDI_LISTENER_FACTORY_DELAYED_CLASS = "org.hibernate.jpa.event.internal.jpa.ListenerFactoryBeanManagerDelayedImpl"; + private static final String CDI_LISTENER_FACTORY_METHOD_NAME = "fromBeanManagerReference"; + + + private static ListenerFactory buildStandardBeanManagerListenerFactory(Object beanManagerRef) { + return buildBeanManagerListenerFactory( beanManagerRef, CDI_LISTENER_FACTORY_STANDARD_CLASS ); + } + + private static ListenerFactory buildDelayedBeanManagerListenerFactory(Object beanManagerRef) { + return buildBeanManagerListenerFactory( beanManagerRef, CDI_LISTENER_FACTORY_DELAYED_CLASS ); + } + + @SuppressWarnings("unchecked") + private static ListenerFactory buildBeanManagerListenerFactory( + Object beanManagerRef, + String listenerClass) { + try { + // specifically using our ClassLoader here... + final Class beanManagerListenerFactoryClass = ListenerFactoryBuilder.class.getClassLoader() + .loadClass( listenerClass ); + final Method beanManagerListenerFactoryBuilderMethod = beanManagerListenerFactoryClass.getMethod( + CDI_LISTENER_FACTORY_METHOD_NAME, + Object.class + ); + + try { + return (ListenerFactory) beanManagerListenerFactoryBuilderMethod.invoke( null, beanManagerRef ); + } + catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } + catch (ClassNotFoundException e) { + throw new HibernateException( + "Could not locate BeanManager ListenerFactory class [" + listenerClass + "] to handle CDI extensions", + e + ); + } + catch (HibernateException e) { + throw e; + } + catch (Throwable e) { + throw new HibernateException( + "Could not access BeanManager ListenerFactory class [" + listenerClass + "] to handle CDI extensions", + e + ); + } + } +} + diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BaseCDIIntegrationTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BaseCdiIntegrationTest.java similarity index 56% rename from hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BaseCDIIntegrationTest.java rename to hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BaseCdiIntegrationTest.java index 58a8e608a5..4f15fff7e6 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BaseCDIIntegrationTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BaseCdiIntegrationTest.java @@ -1,3 +1,27 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2016, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ + /* * Hibernate, Relational Persistence for Idiomatic Java * @@ -18,9 +42,13 @@ import org.jboss.weld.bootstrap.api.Environments; /** * @author Steve Ebersole */ -public abstract class BaseCDIIntegrationTest extends BaseEntityManagerFunctionalTestCase { +public abstract class BaseCdiIntegrationTest extends BaseEntityManagerFunctionalTestCase { private TestContainer testContainer; + public TestContainer getTestContainer() { + return testContainer; + } + @Override @SuppressWarnings("unchecked") protected void addConfigOptions(Map options) { diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BasicCDITest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BasicCdiTest.java similarity index 72% rename from hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BasicCDITest.java rename to hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BasicCdiTest.java index 5fe8af7fa4..be6b143bb0 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BasicCDITest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BasicCdiTest.java @@ -1,3 +1,27 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2016, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ + /* * Hibernate, Relational Persistence for Idiomatic Java * @@ -6,9 +30,10 @@ */ package org.hibernate.jpa.test.cdi; -import javax.enterprise.context.spi.CreationalContext; -import javax.enterprise.inject.spi.AnnotatedType; -import javax.enterprise.inject.spi.InjectionTarget; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; import javax.inject.Inject; import javax.persistence.Entity; import javax.persistence.EntityListeners; @@ -16,10 +41,6 @@ import javax.persistence.EntityManager; import javax.persistence.Id; import javax.persistence.PrePersist; import javax.persistence.Table; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; import org.junit.Test; @@ -28,7 +49,7 @@ import static org.junit.Assert.assertEquals; /** * @author Steve Ebersole */ -public class BasicCDITest extends BaseCDIIntegrationTest { +public class BasicCdiTest extends BaseCdiIntegrationTest { private static int count; @Override diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/DelayedCdiTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/DelayedCdiTest.java new file mode 100644 index 0000000000..c180ebc53e --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/DelayedCdiTest.java @@ -0,0 +1,427 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2016, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ + +/* + * 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.test.cdi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.el.ELResolver; +import javax.el.ExpressionFactory; +import javax.enterprise.context.spi.Context; +import javax.enterprise.context.spi.Contextual; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.AnnotatedField; +import javax.enterprise.inject.spi.AnnotatedMember; +import javax.enterprise.inject.spi.AnnotatedMethod; +import javax.enterprise.inject.spi.AnnotatedParameter; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanAttributes; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.Decorator; +import javax.enterprise.inject.spi.Extension; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.inject.spi.InjectionTarget; +import javax.enterprise.inject.spi.InjectionTargetFactory; +import javax.enterprise.inject.spi.InterceptionType; +import javax.enterprise.inject.spi.Interceptor; +import javax.enterprise.inject.spi.ObserverMethod; +import javax.enterprise.inject.spi.ProducerFactory; +import javax.inject.Inject; +import javax.persistence.Entity; +import javax.persistence.EntityListeners; +import javax.persistence.EntityManager; +import javax.persistence.Id; +import javax.persistence.PrePersist; +import javax.persistence.Table; + +import org.hibernate.jpa.AvailableSettings; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Steve Ebersole + */ +public class DelayedCdiTest extends BaseCdiIntegrationTest { + private BeanManagerImpl beanManager = new BeanManagerImpl(); + + private static int count; + + @Override + public Class[] getCdiBeans() { + return new Class[] { EventQueue.class }; + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { MyEntity.class }; + } + + @Override + protected void addConfigOptions(Map options) { + options.put( AvailableSettings.DELAY_CDI_ACCESS, "true" ); + super.addConfigOptions( options ); + } + + @Override + protected BeanManager getBeanManager() { + // this is the BeanManager used to bootstrap Hibernate. Here + // we want this to be our custom BeanManagerImpl + return beanManager; + } + + @Override + protected void afterEntityManagerFactoryBuilt() { + beanManager.delegate = getTestContainer().getBeanManager( getTestContainer().getDeployment().getBeanDeploymentArchives().iterator().next() ); + } + + @Test + @SuppressWarnings("unchecked") + public void testIt() { + count = 0; + + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + em.persist( new MyEntity( 1 ) ); + em.getTransaction().commit(); + em.close(); + + assertEquals( 1, count ); + + em = getOrCreateEntityManager(); + em.getTransaction().begin(); + em.remove( em.getReference( MyEntity.class, 1 ) ); + em.getTransaction().commit(); + em.close(); + } + + @Entity + @EntityListeners( Monitor.class ) + @Table(name = "my_entity") + public static class MyEntity { + private Integer id; + private String name; + + public MyEntity() { + } + + public MyEntity(Integer id) { + this.id = id; + } + + @Id + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + public static class EventQueue { + private List events; + + public void addEvent(Event anEvent) { + if ( events == null ) { + events = new ArrayList(); + } + events.add( anEvent ); + count++; + } + } + + public static class Event { + private final String who; + private final String what; + private final String when; + + public Event(String who, String what, String when) { + this.who = who; + this.what = what; + this.when = when; + } + + public String getWho() { + return who; + } + + public String getWhat() { + return what; + } + + public String getWhen() { + return when; + } + } + + public static class Monitor { + private final EventQueue eventQueue; + + @Inject + public Monitor(EventQueue eventQueue) { + this.eventQueue = eventQueue; + } + + @PrePersist + public void onCreate(Object entity) { + eventQueue.addEvent( + new Event( entity.toString(), "created", now() ) + ); + } + + private String now() { + return new SimpleDateFormat().format( new Date() ); + } + } + + public static class BeanManagerImpl implements BeanManager { + private BeanManager delegate; + + @Override + public Object getReference(Bean bean, Type beanType, CreationalContext ctx) { + return getDelegate().getReference( bean, beanType, ctx ); + } + + private BeanManager getDelegate() { + if ( delegate == null ) { + throw new RuntimeException( "Real BeanManager not yet known" ); + } + + return delegate; + } + + @Override + public Object getInjectableReference(InjectionPoint ij, CreationalContext ctx) { + return getDelegate().getInjectableReference( ij, ctx ); + } + + @Override + public CreationalContext createCreationalContext(Contextual contextual) { + return getDelegate().createCreationalContext( contextual ); + } + + @Override + public Set> getBeans(Type beanType, Annotation... qualifiers) { + return getDelegate().getBeans( beanType, qualifiers ); + } + + @Override + public Set> getBeans(String name) { + return getDelegate().getBeans( name ); + } + + @Override + public Bean getPassivationCapableBean(String id) { + return getDelegate().getPassivationCapableBean( id ); + } + + @Override + public Bean resolve(Set> beans) { + return getDelegate().resolve( beans ); + } + + @Override + public void validate(InjectionPoint injectionPoint) { + getDelegate().validate( injectionPoint ); + } + + @Override + public void fireEvent(Object event, Annotation... qualifiers) { + getDelegate().fireEvent( event, qualifiers ); + } + + @Override + public Set> resolveObserverMethods(T event, Annotation... qualifiers) { + return getDelegate().resolveObserverMethods( event, qualifiers ); + } + + @Override + public List> resolveDecorators(Set types, Annotation... qualifiers) { + return getDelegate().resolveDecorators( types, qualifiers ); + } + + @Override + public List> resolveInterceptors(InterceptionType type, Annotation... interceptorBindings) { + return getDelegate().resolveInterceptors( type, interceptorBindings ); + } + + @Override + public boolean isScope(Class annotationType) { + return getDelegate().isScope( annotationType ); + } + + @Override + public boolean isNormalScope(Class annotationType) { + return getDelegate().isNormalScope( annotationType ); + } + + @Override + public boolean isPassivatingScope(Class annotationType) { + return getDelegate().isPassivatingScope( annotationType ); + } + + @Override + public boolean isQualifier(Class annotationType) { + return getDelegate().isQualifier( annotationType ); + } + + @Override + public boolean isInterceptorBinding(Class annotationType) { + return getDelegate().isInterceptorBinding( annotationType ); + } + + @Override + public boolean isStereotype(Class annotationType) { + return getDelegate().isStereotype( annotationType ); + } + + @Override + public Set getInterceptorBindingDefinition(Class bindingType) { + return getDelegate().getInterceptorBindingDefinition( bindingType ); + } + + @Override + public Set getStereotypeDefinition(Class stereotype) { + return getDelegate().getStereotypeDefinition( stereotype ); + } + + @Override + public boolean areQualifiersEquivalent(Annotation qualifier1, Annotation qualifier2) { + return getDelegate().areQualifiersEquivalent( qualifier1, qualifier2 ); + } + + @Override + public boolean areInterceptorBindingsEquivalent(Annotation interceptorBinding1, Annotation interceptorBinding2) { + return getDelegate().areInterceptorBindingsEquivalent( interceptorBinding1, interceptorBinding2 ); + } + + @Override + public int getQualifierHashCode(Annotation qualifier) { + return getDelegate().getQualifierHashCode( qualifier ); + } + + @Override + public int getInterceptorBindingHashCode(Annotation interceptorBinding) { + return getDelegate().getInterceptorBindingHashCode( interceptorBinding ); + } + + @Override + public Context getContext(Class scopeType) { + return getDelegate().getContext( scopeType ); + } + + @Override + public ELResolver getELResolver() { + return getDelegate().getELResolver(); + } + + @Override + public ExpressionFactory wrapExpressionFactory(ExpressionFactory expressionFactory) { + return getDelegate().wrapExpressionFactory( expressionFactory ); + } + + @Override + public AnnotatedType createAnnotatedType(Class type) { + return getDelegate().createAnnotatedType( type ); + } + + @Override + public InjectionTarget createInjectionTarget(AnnotatedType type) { + return getDelegate().createInjectionTarget( type ); + } + + @Override + public InjectionTargetFactory getInjectionTargetFactory(AnnotatedType annotatedType) { + return getDelegate().getInjectionTargetFactory( annotatedType ); + } + + @Override + public ProducerFactory getProducerFactory(AnnotatedField field) { + return getDelegate().getProducerFactory( field ); + } + + @Override + public ProducerFactory getProducerFactory(AnnotatedMethod method) { + return getDelegate().getProducerFactory( method ); + } + + @Override + public BeanAttributes createBeanAttributes(AnnotatedType type) { + return getDelegate().createBeanAttributes( type ); + } + + @Override + public BeanAttributes createBeanAttributes(AnnotatedMember type) { + return getDelegate().createBeanAttributes( type ); + } + + @Override + public Bean createBean(BeanAttributes attributes, Class beanClass, InjectionTargetFactory injectionTargetFactory) { + return getDelegate().createBean( attributes, beanClass, injectionTargetFactory ); + } + + @Override + public Bean createBean(BeanAttributes attributes, Class beanClass, ProducerFactory producerFactory) { + return getDelegate().createBean( attributes, beanClass, producerFactory ); + } + + @Override + public InjectionPoint createInjectionPoint(AnnotatedField field) { + return getDelegate().createInjectionPoint( field ); + } + + @Override + public InjectionPoint createInjectionPoint(AnnotatedParameter parameter) { + return getDelegate().createInjectionPoint( parameter ); + } + + @Override + public T getExtension(Class extensionClass) { + return getDelegate().getExtension( extensionClass ); + } + } +}