HHH-8706 - Delay registering of the entity listener class as long as possible
This commit is contained in:
parent
6076298ce9
commit
a5271d6e76
|
@ -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 )
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
|
@ -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
|
|
@ -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" );
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue