HHH-8706 - Delay registering of the entity listener class as long as possible

This commit is contained in:
Steve Ebersole 2016-01-23 12:00:45 -06:00
parent 6076298ce9
commit a5271d6e76
12 changed files with 750 additions and 215 deletions

View File

@ -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 )

View File

@ -1,161 +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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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.HibernateException;
import org.hibernate.jpa.event.spi.jpa.ExtendedBeanManager;
import org.hibernate.jpa.event.spi.jpa.Listener;
import org.hibernate.jpa.event.spi.jpa.ListenerFactory;
/**
* CDI-based implementation of the ListenerFactory contract. Listener instances are
* kept in a map keyed by Class to ensure single-instance-ness.
*
* @author Steve Ebersole
*/
public class ListenerFactoryBeanManagerImpl implements ListenerFactory, ExtendedBeanManager.LifecycleListener {
private final BeanManager beanManager;
private final boolean extendedBm;
private final Map<Class,ListenerImplementor> listenerMap = new ConcurrentHashMap<Class, ListenerImplementor>();
public static ListenerFactoryBeanManagerImpl fromBeanManagerReference(Object reference) {
return new ListenerFactoryBeanManagerImpl( (BeanManager) reference );
}
public ListenerFactoryBeanManagerImpl(BeanManager beanManager) {
this.beanManager = beanManager;
if ( beanManager instanceof ExtendedBeanManager ) {
( (ExtendedBeanManager) beanManager ).registerLifecycleListener( this );
extendedBm = true;
}
else {
extendedBm = false;
}
}
@Override
@SuppressWarnings("unchecked")
public <T> Listener<T> buildListener(Class<T> listenerClass) {
ListenerImplementor listenerImpl = listenerMap.get( listenerClass );
if ( listenerImpl == null ) {
listenerImpl = makeListener( listenerClass );
listenerMap.put( listenerClass, listenerImpl );
}
return (Listener<T>) listenerImpl;
}
private <T> ListenerImplementor makeListener(Class<T> listenerClass) {
if ( extendedBm ) {
return new ListenerExtendedImpl<T>( listenerClass );
}
else {
return new ListenerBasicImpl<T>( listenerClass );
}
}
@Override
public void release() {
for ( ListenerImplementor listenerImpl : listenerMap.values() ) {
listenerImpl.release();
}
listenerMap.clear();
}
@Override
public void beanManagerInitialized() {
for ( ListenerImplementor listenerImpl : listenerMap.values() ) {
// if the entries are not ListenerExtendedImpl instances we have serious issues...
( (ListenerExtendedImpl) listenerImpl ).initialize();
}
}
private interface ListenerImplementor<T> extends Listener<T> {
void release();
}
private class ListenerBasicImpl<T> implements ListenerImplementor<T> {
private final InjectionTarget<T> injectionTarget;
private final CreationalContext<T> creationalContext;
private final T listenerInstance;
private ListenerBasicImpl(Class<T> listenerClass) {
AnnotatedType<T> 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();
}
}
private class ListenerExtendedImpl<T> implements ListenerImplementor<T> {
private final Class<T> listenerClass;
private boolean initialized = false;
private InjectionTarget<T> injectionTarget;
private CreationalContext<T> creationalContext;
private T listenerInstance;
private ListenerExtendedImpl(Class<T> listenerClass) {
this.listenerClass = listenerClass;
}
public void initialize() {
AnnotatedType<T> 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 ) {
throw new HibernateException( "CDI not initialized as expected" );
}
return listenerInstance;
}
public void release() {
if ( !initialized ) {
// log
return;
}
injectionTarget.preDestroy( listenerInstance );
injectionTarget.dispose( listenerInstance );
creationalContext.release();
}
}
}

View File

@ -0,0 +1,125 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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.HibernateException;
import org.hibernate.jpa.event.spi.jpa.ExtendedBeanManager;
import org.hibernate.jpa.event.spi.jpa.Listener;
import org.hibernate.jpa.event.spi.jpa.ListenerFactory;
/**
* CDI-based implementation of the ListenerFactory contract. Further, this
* implementation leverages the ExtendedBeanManager contract to delay CDI calls.
*
* @author Steve Ebersole
*/
@SuppressWarnings("unused")
public class ListenerFactoryBeanManagerLazyImpl implements ListenerFactory, ExtendedBeanManager.LifecycleListener {
private final Map<Class,ListenerImpl> listenerMap = new ConcurrentHashMap<Class, ListenerImpl>();
/**
* Used via reflection from JpaIntegrator, 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
*/
@SuppressWarnings("unused")
public static ListenerFactoryBeanManagerLazyImpl fromBeanManagerReference(Object reference) {
return new ListenerFactoryBeanManagerLazyImpl( (BeanManager) reference );
}
public ListenerFactoryBeanManagerLazyImpl(BeanManager beanManager) {
if ( !ExtendedBeanManager.class.isInstance( beanManager ) ) {
throw new IllegalArgumentException(
"Expecting BeanManager reference that implements optional ExtendedBeanManager contract : " +
beanManager
);
}
( (ExtendedBeanManager) beanManager ).registerLifecycleListener( this );
}
@Override
@SuppressWarnings("unchecked")
public <T> Listener<T> buildListener(Class<T> listenerClass) {
ListenerImpl listenerImpl = listenerMap.get( listenerClass );
if ( listenerImpl == null ) {
listenerImpl = new ListenerImpl( listenerClass );
listenerMap.put( listenerClass, listenerImpl );
}
return (Listener<T>) listenerImpl;
}
@Override
public void release() {
for ( ListenerImpl listenerImpl : listenerMap.values() ) {
listenerImpl.release();
}
listenerMap.clear();
}
@Override
public void beanManagerInitialized(BeanManager beanManager) {
for ( ListenerImpl listenerImpl : listenerMap.values() ) {
listenerImpl.initialize( beanManager );
}
}
private static class ListenerImpl<T> implements Listener<T> {
private final Class<T> listenerClass;
private boolean initialized = false;
private InjectionTarget<T> injectionTarget;
private CreationalContext<T> creationalContext;
private T listenerInstance;
private ListenerImpl(Class<T> listenerClass) {
this.listenerClass = listenerClass;
}
public void initialize(BeanManager beanManager) {
AnnotatedType<T> 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 ) {
throw new HibernateException( "CDI not initialized as expected" );
}
return listenerInstance;
}
public void release() {
if ( !initialized ) {
// log
return;
}
injectionTarget.preDestroy( listenerInstance );
injectionTarget.dispose( listenerInstance );
creationalContext.release();
}
}
}

View File

@ -0,0 +1,99 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
/**
* CDI-based implementation of the ListenerFactory contract. This CDI-based
* implementation works in the JPA standard prescribed manner.
* <p/>
* See {@link ListenerFactoryBeanManagerLazyImpl} for an alt implementation that
* is still JPA compliant, but that works based on delayed CDI calls. Works
* on a non-CDI-defined CDI extension; we plan to propose this extension to the
* CDI expert group for the next CDI iteration
*
* @author Steve Ebersole
*/
@SuppressWarnings("unused")
public class ListenerFactoryBeanManagerStandardImpl implements ListenerFactory {
private final BeanManager beanManager;
private final Map<Class,ListenerImpl> listenerMap = new ConcurrentHashMap<Class, ListenerImpl>();
/**
* Used via reflection from JpaIntegrator, 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
*/
@SuppressWarnings("unused")
public static ListenerFactoryBeanManagerStandardImpl fromBeanManagerReference(Object reference) {
return new ListenerFactoryBeanManagerStandardImpl( (BeanManager) reference );
}
public ListenerFactoryBeanManagerStandardImpl(BeanManager beanManager) {
this.beanManager = beanManager;
}
@Override
@SuppressWarnings("unchecked")
public <T> Listener<T> buildListener(Class<T> listenerClass) {
ListenerImpl listenerImpl = listenerMap.get( listenerClass );
if ( listenerImpl == null ) {
listenerImpl = new ListenerImpl( listenerClass );
listenerMap.put( listenerClass, listenerImpl );
}
return (Listener<T>) listenerImpl;
}
@Override
public void release() {
for ( ListenerImpl listenerImpl : listenerMap.values() ) {
listenerImpl.release();
}
listenerMap.clear();
}
private class ListenerImpl<T> implements Listener<T> {
private final InjectionTarget<T> injectionTarget;
private final CreationalContext<T> creationalContext;
private final T listenerInstance;
private ListenerImpl(Class<T> listenerClass) {
AnnotatedType<T> 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();
}
}
}

View File

@ -13,19 +13,17 @@ 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. Listener instances
* are kept in a map keyed by Class to achieve singleton-ness.
* Standard implementation of the ListenerFactory contract using simple instantiation.
*
* @author Steve Ebersole
*/
public class ListenerFactoryStandardImpl implements ListenerFactory {
private final ConcurrentHashMap<Class,Listener> listenerInstances = new ConcurrentHashMap<Class,Listener>();
private final ConcurrentHashMap<Class,ListenerImpl> listenerInstances = new ConcurrentHashMap<Class,ListenerImpl>();
@Override
@SuppressWarnings("unchecked")
public <T> Listener<T> buildListener(Class<T> listenerClass) {
Listener listenerImpl = listenerInstances.get( listenerClass );
ListenerImpl listenerImpl = listenerInstances.get( listenerClass );
if ( listenerImpl == null ) {
try {
T listenerInstance = listenerClass.newInstance();
@ -37,7 +35,7 @@ public class ListenerFactoryStandardImpl implements ListenerFactory {
e
);
}
Listener existing = listenerInstances.putIfAbsent(
ListenerImpl existing = listenerInstances.putIfAbsent(
listenerClass,
listenerImpl
);

View File

@ -46,6 +46,7 @@ import org.hibernate.jpa.event.internal.jpa.CallbackRegistryImpl;
import org.hibernate.jpa.event.internal.jpa.ListenerFactoryStandardImpl;
import org.hibernate.jpa.event.spi.jpa.EntityCallbackBuilder;
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;
@ -126,12 +127,8 @@ public class JpaIntegrator implements Integrator {
.getReflectionManager();
this.callbackRegistry = new CallbackRegistryImpl();
final Object beanManagerRef = sessionFactory.getSessionFactoryOptions().getBeanManagerReference();
this.jpaListenerFactory = beanManagerRef == null
? new ListenerFactoryStandardImpl()
: buildBeanManagerListenerFactory( beanManagerRef );
this.jpaListenerFactory = ListenerFactoryBuilder.buildListenerFactory( sessionFactory.getSessionFactoryOptions() );
this.entityCallbackBuilder = new EntityCallbackBuilderLegacyImpl( jpaListenerFactory, reflectionManager );
for ( PersistentClass persistentClass : metadata.getEntityBindings() ) {
if ( persistentClass.getClassName() == null ) {
// we can have non java class persisted by hibernate
@ -150,42 +147,6 @@ public class JpaIntegrator implements Integrator {
}
}
private static final String CDI_LISTENER_FACTORY_CLASS = "org.hibernate.jpa.event.internal.jpa.ListenerFactoryBeanManagerImpl";
private ListenerFactory buildBeanManagerListenerFactory(Object beanManagerRef) {
try {
// specifically using our classloader here...
final Class beanManagerListenerFactoryClass = getClass().getClassLoader()
.loadClass( CDI_LISTENER_FACTORY_CLASS );
final Method beanManagerListenerFactoryBuilderMethod = beanManagerListenerFactoryClass.getMethod(
"fromBeanManagerReference",
Object.class
);
try {
return (ListenerFactory) beanManagerListenerFactoryBuilderMethod.invoke( null, beanManagerRef );
}
catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
catch (ClassNotFoundException e) {
throw new HibernateException(
"Could not locate BeanManagerListenerFactory class to handle CDI extensions",
e
);
}
catch (HibernateException e) {
throw e;
}
catch (Throwable e) {
throw new HibernateException(
"Could not access BeanManagerListenerFactory class to handle CDI extensions",
e
);
}
}
@Override
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
if ( callbackRegistry != null ) {

View File

@ -6,15 +6,32 @@
*/
package org.hibernate.jpa.event.spi.jpa;
import javax.enterprise.inject.spi.BeanManager;
/**
* This contract and the nested LifecycleListener contract represent the changes
* we'd like to propose to the CDI spec. The idea being simply to allow contextual
* registration of BeanManager lifecycle callbacks
*
* @author Steve Ebersole
*/
public interface ExtendedBeanManager {
/**
* Register a BeanManager LifecycleListener
*
* @param lifecycleListener The listener to register
*/
void registerLifecycleListener(LifecycleListener lifecycleListener);
/**
* Contract for things interested in receiving notifications of
* BeanManager lifecycle events.
* <p/>
* A "beanManagerDestroyed" notifications would probably also be generally
* useful, although we do not need it here and not sure WildFly can really
* tell us that reliably.
*/
interface LifecycleListener {
void beanManagerInitialized();
void beanManagerInitialized(BeanManager beanManager);
}
}

View File

@ -7,7 +7,12 @@
package org.hibernate.jpa.event.spi.jpa;
/**
* Factory for building instances of callback listener classes.
* Contract for building instances of JPA callback listener classes.
* <p/>
* 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
*/

View File

@ -0,0 +1,81 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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.jpa.event.internal.jpa.ListenerFactoryStandardImpl;
/**
* @author Steve Ebersole
*/
public class ListenerFactoryBuilder {
public static ListenerFactory buildListenerFactory(SessionFactoryOptions options) {
final Object beanManagerRef = options.getBeanManagerReference();
if ( beanManagerRef == null ) {
return new ListenerFactoryStandardImpl();
}
else if ( ExtendedBeanManager.class.isInstance( beanManagerRef ) ) {
return buildLazyBeanManagerListenerFactory( beanManagerRef );
}
else {
return buildStandardBeanManagerListenerFactory( beanManagerRef );
}
}
private static final String CDI_LISTENER_FACTORY_CLASS = "org.hibernate.jpa.event.internal.jpa.ListenerFactoryBeanManagerStandardImpl";
private static final String CDI_LISTENER_FACTORY_LAZY_CLASS = "org.hibernate.jpa.event.internal.jpa.ListenerFactoryBeanManagerLazyImpl";
private static final String CDI_LISTENER_FACTORY_METHOD_NAME = "fromBeanManagerReference";
private static ListenerFactory buildStandardBeanManagerListenerFactory(Object beanManagerRef) {
return buildBeanManagerListenerFactory( beanManagerRef, CDI_LISTENER_FACTORY_CLASS );
}
private static ListenerFactory buildLazyBeanManagerListenerFactory(Object beanManagerRef) {
return buildBeanManagerListenerFactory( beanManagerRef, CDI_LISTENER_FACTORY_LAZY_CLASS );
}
@SuppressWarnings("unchecked")
private static ListenerFactory buildBeanManagerListenerFactory(Object beanManagerRef, String factoryClassName) {
try {
// specifically using our ClassLoader here...
final Class beanManagerListenerFactoryClass = ListenerFactoryBuilder.class.getClassLoader()
.loadClass( factoryClassName );
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 [" + factoryClassName + "] to handle CDI extensions",
e
);
}
catch (HibernateException e) {
throw e;
}
catch (Throwable e) {
throw new HibernateException(
"Could not access BeanManagerListenerFactory class to handle CDI extensions",
e
);
}
}
}

View File

@ -18,9 +18,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) {

View File

@ -6,9 +6,6 @@
*/
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 javax.inject.Inject;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
@ -28,7 +25,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

View File

@ -0,0 +1,405 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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.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.event.spi.jpa.ExtendedBeanManager;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* @author Steve Ebersole
*/
public class LazyCdiTest extends BaseCdiIntegrationTest {
ExtendedBeanManagerImpl extendedBeanManager = new ExtendedBeanManagerImpl();
private static int count;
@Override
public Class[] getCdiBeans() {
return new Class[] { EventQueue.class };
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { MyEntity.class };
}
@Override
protected BeanManager getBeanManager() {
// this is the BeanManager used to bootstrap Hibernate. Here
// we want this to be our custom ExtendedBeanManagerImpl
return extendedBeanManager;
}
@Override
protected void afterEntityManagerFactoryBuilt() {
// now come back and do the ExtendedBeanManagerImpl lifecycle callbacks
final BeanManager realBeanManager = getTestContainer().getBeanManager( getTestContainer().getDeployment().getBeanDeploymentArchives().iterator().next() );
for ( ExtendedBeanManager.LifecycleListener lifecycleListener : extendedBeanManager.getLifecycleListeners() ) {
lifecycleListener.beanManagerInitialized( realBeanManager );
}
extendedBeanManager.getLifecycleListeners().clear();
}
@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<Event> events;
public void addEvent(Event anEvent) {
if ( events == null ) {
events = new ArrayList<Event>();
}
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 class ExtendedBeanManagerImpl implements BeanManager, ExtendedBeanManager {
private List<LifecycleListener> lifecycleListeners = new ArrayList<LifecycleListener>();
@Override
public void registerLifecycleListener(LifecycleListener lifecycleListener) {
lifecycleListeners.add( lifecycleListener );
}
public List<LifecycleListener> getLifecycleListeners() {
return lifecycleListeners;
}
// ~~~~
@Override
public Object getReference(Bean<?> bean, Type beanType, CreationalContext<?> ctx) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public Object getInjectableReference(InjectionPoint ij, CreationalContext<?> ctx) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public <T> CreationalContext<T> createCreationalContext(Contextual<T> contextual) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public Set<Bean<?>> getBeans(Type beanType, Annotation... qualifiers) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public Set<Bean<?>> getBeans(String name) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public Bean<?> getPassivationCapableBean(String id) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public <X> Bean<? extends X> resolve(Set<Bean<? extends X>> beans) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public void validate(InjectionPoint injectionPoint) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public void fireEvent(Object event, Annotation... qualifiers) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public <T> Set<ObserverMethod<? super T>> resolveObserverMethods(T event, Annotation... qualifiers) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public List<Decorator<?>> resolveDecorators(Set<Type> types, Annotation... qualifiers) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public List<Interceptor<?>> resolveInterceptors(InterceptionType type, Annotation... interceptorBindings) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public boolean isScope(Class<? extends Annotation> annotationType) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public boolean isNormalScope(Class<? extends Annotation> annotationType) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public boolean isPassivatingScope(Class<? extends Annotation> annotationType) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public boolean isQualifier(Class<? extends Annotation> annotationType) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public boolean isInterceptorBinding(Class<? extends Annotation> annotationType) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public boolean isStereotype(Class<? extends Annotation> annotationType) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public Set<Annotation> getInterceptorBindingDefinition(Class<? extends Annotation> bindingType) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public Set<Annotation> getStereotypeDefinition(Class<? extends Annotation> stereotype) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public boolean areQualifiersEquivalent(Annotation qualifier1, Annotation qualifier2) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public boolean areInterceptorBindingsEquivalent(Annotation interceptorBinding1, Annotation interceptorBinding2) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public int getQualifierHashCode(Annotation qualifier) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public int getInterceptorBindingHashCode(Annotation interceptorBinding) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public Context getContext(Class<? extends Annotation> scopeType) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public ELResolver getELResolver() {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public ExpressionFactory wrapExpressionFactory(ExpressionFactory expressionFactory) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public <T> AnnotatedType<T> createAnnotatedType(Class<T> type) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public <T> InjectionTarget<T> createInjectionTarget(AnnotatedType<T> type) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public <T> InjectionTargetFactory<T> getInjectionTargetFactory(AnnotatedType<T> annotatedType) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public <X> ProducerFactory<X> getProducerFactory(AnnotatedField<? super X> field) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public <X> ProducerFactory<X> getProducerFactory(AnnotatedMethod<? super X> method) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public <T> BeanAttributes<T> createBeanAttributes(AnnotatedType<T> type) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public BeanAttributes<?> createBeanAttributes(AnnotatedMember<?> type) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public <T> Bean<T> createBean(BeanAttributes<T> attributes, Class<T> beanClass, InjectionTargetFactory<T> injectionTargetFactory) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public <T> Bean<T> createBean(BeanAttributes<T> attributes, Class<?> beanClass, ProducerFactory<T> producerFactory) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public InjectionPoint createInjectionPoint(AnnotatedField<?> field) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public InjectionPoint createInjectionPoint(AnnotatedParameter<?> parameter) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
@Override
public <T extends Extension> T getExtension(Class<T> extensionClass) {
throw new UnsupportedOperationException( "ExtendedBeanManagerImpl here just to gainn access to BeanManager lazily" );
}
}
}