HHH-7387 - Integrate Draft 6 of the JPA 2.1 spec : CDI support

This commit is contained in:
Steve Ebersole 2012-08-17 18:19:25 -05:00
parent d252a141d0
commit f74cf0b83f
34 changed files with 1093 additions and 415 deletions

View File

@ -9,11 +9,16 @@ dependencies {
compile( libraries.jpa ) compile( libraries.jpa )
compile( libraries.jta ) compile( libraries.jta )
compile( libraries.javassist ) compile( libraries.javassist )
provided( "javax.enterprise:cdi-api:1.0-SP4" )
testCompile( project(':hibernate-testing') ) testCompile( project(':hibernate-testing') )
testCompile( libraries.shrinkwrap_api ) testCompile( libraries.shrinkwrap_api )
testCompile( libraries.shrinkwrap ) testCompile( libraries.shrinkwrap )
testCompile( libraries.validation ) testCompile( libraries.validation )
testRuntime( libraries.validator ) testRuntime( libraries.validator )
testCompile( "org.jboss.weld.arquillian.container:arquillian-weld-ee-embedded-1.1:1.1.2.Final" )
testCompile( "org.jboss.weld:weld-core:1.1.9.Final" )
testRuntime( "org.glassfish.web:el-impl:2.1.2-b04" )
testRuntime( "org.jboss.ejb3:jboss-ejb3-api:3.1.0" )
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -187,6 +187,11 @@ public interface AvailableSettings {
*/ */
public static final String REMOVE_VALIDATION_GROUP = "javax.persistence.validation.group.pre-remove"; public static final String REMOVE_VALIDATION_GROUP = "javax.persistence.validation.group.pre-remove";
/**
* Used to pass along the CDI BeanManager, if any, to be used.
*/
public static final String CDI_BEAN_MANAGER = "javax.persistence.bean.manager";
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Hibernate specific settings // Hibernate specific settings

View File

@ -1,90 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009-2011, 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
*/
package org.hibernate.jpa.internal.event;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.hibernate.internal.util.ReflectHelper;
/**
* @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
*/
public abstract class Callback implements Serializable {
transient protected Method callbackMethod;
public Callback(Method callbackMethod) {
this.callbackMethod = callbackMethod;
}
public Method getCallbackMethod() {
return callbackMethod;
}
public abstract void invoke(Object bean);
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject( callbackMethod.toGenericString() );
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
String signature = (String) ois.readObject();
StringTokenizer st = new StringTokenizer( signature, " ", false );
String usefulSignature = null;
while ( st.hasMoreElements() ) usefulSignature = (String) st.nextElement();
int parenthesis = usefulSignature.indexOf( "(" );
String methodAndClass = usefulSignature.substring( 0, parenthesis );
int lastDot = methodAndClass.lastIndexOf( "." );
String clazzName = methodAndClass.substring( 0, lastDot );
Class callbackClass = ReflectHelper.classForName( clazzName, this.getClass() );
String parametersString = usefulSignature.substring( parenthesis + 1, usefulSignature.length() - 1 );
st = new StringTokenizer( parametersString, ", ", false );
List<Class> parameters = new ArrayList<Class>();
while ( st.hasMoreElements() ) {
String parameter = (String) st.nextElement();
parameters.add( ReflectHelper.classForName( parameter, this.getClass() ) );
}
String methodName = methodAndClass.substring( lastDot + 1, methodAndClass.length() );
try {
callbackMethod = callbackClass.getDeclaredMethod(
methodName,
parameters.toArray( new Class[ parameters.size() ] )
);
if ( ! callbackMethod.isAccessible() ) {
callbackMethod.setAccessible( true );
}
}
catch (NoSuchMethodException e) {
throw new IOException( "Unable to get EJB3 callback method: " + signature + ", cause: " + e );
}
}
}

View File

@ -23,12 +23,11 @@
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event;
import javax.enterprise.inject.spi.BeanManager;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.engine.spi.CascadeStyles; import org.hibernate.engine.spi.CascadeStyles;
@ -41,6 +40,28 @@ import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType; import org.hibernate.event.spi.EventType;
import org.hibernate.integrator.spi.Integrator; import org.hibernate.integrator.spi.Integrator;
import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.internal.event.core.HibernateEntityManagerEventListener;
import org.hibernate.jpa.internal.event.core.JpaAutoFlushEventListener;
import org.hibernate.jpa.internal.event.core.JpaDeleteEventListener;
import org.hibernate.jpa.internal.event.core.JpaFlushEntityEventListener;
import org.hibernate.jpa.internal.event.core.JpaFlushEventListener;
import org.hibernate.jpa.internal.event.core.JpaMergeEventListener;
import org.hibernate.jpa.internal.event.core.JpaPersistEventListener;
import org.hibernate.jpa.internal.event.core.JpaPersistOnFlushEventListener;
import org.hibernate.jpa.internal.event.core.JpaPostDeleteEventListener;
import org.hibernate.jpa.internal.event.core.JpaPostInsertEventListener;
import org.hibernate.jpa.internal.event.core.JpaPostLoadEventListener;
import org.hibernate.jpa.internal.event.core.JpaPostUpdateEventListener;
import org.hibernate.jpa.internal.event.core.JpaSaveEventListener;
import org.hibernate.jpa.internal.event.core.JpaSaveOrUpdateEventListener;
import org.hibernate.jpa.internal.event.jpa.BeanManagerListenerFactory;
import org.hibernate.jpa.internal.event.jpa.CallbackProcessor;
import org.hibernate.jpa.internal.event.jpa.CallbackProcessorImpl;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistryConsumer;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistry;
import org.hibernate.jpa.internal.event.jpa.LegacyCallbackProcessor;
import org.hibernate.jpa.internal.event.jpa.ListenerFactory;
import org.hibernate.jpa.internal.event.jpa.StandardListenerFactory;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.metamodel.binding.EntityBinding; import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.source.MetadataImplementor; import org.hibernate.metamodel.source.MetadataImplementor;
@ -50,16 +71,19 @@ import org.hibernate.secure.internal.JACCPreLoadEventListener;
import org.hibernate.secure.internal.JACCPreUpdateEventListener; import org.hibernate.secure.internal.JACCPreUpdateEventListener;
import org.hibernate.secure.internal.JACCSecurityListener; import org.hibernate.secure.internal.JACCSecurityListener;
import org.hibernate.service.classloading.spi.ClassLoaderService; import org.hibernate.service.classloading.spi.ClassLoaderService;
import org.hibernate.service.classloading.spi.ClassLoadingException;
import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.service.spi.SessionFactoryServiceRegistry; import org.hibernate.service.spi.SessionFactoryServiceRegistry;
/** /**
* Prepare the HEM-specific event listeners. * Hibernate EntityManager specific Integrator, performing JPA setup.
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class JpaIntegrator implements Integrator { public class JpaIntegrator implements Integrator {
private ListenerFactory jpaListenerFactory;
private CallbackProcessor callbackProcessor;
private CallbackRegistry callbackRegistry;
private static final DuplicationStrategy JPA_DUPLICATION_STRATEGY = new DuplicationStrategy() { private static final DuplicationStrategy JPA_DUPLICATION_STRATEGY = new DuplicationStrategy() {
@Override @Override
public boolean areMatch(Object listener, Object original) { public boolean areMatch(Object listener, Object original) {
@ -109,6 +133,7 @@ public class JpaIntegrator implements Integrator {
} }
); );
// then prepare listeners // then prepare listeners
final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class ); final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
@ -159,28 +184,30 @@ public class JpaIntegrator implements Integrator {
} }
} }
final EntityCallbackHandler callbackHandler = new EntityCallbackHandler(); // handle JPA "entity listener classes"...
this.callbackRegistry = new CallbackRegistry();
final BeanManager beanManager = (BeanManager) configuration.getProperties().get( AvailableSettings.CDI_BEAN_MANAGER );
this.jpaListenerFactory = beanManager == null
? new StandardListenerFactory()
: new BeanManagerListenerFactory( beanManager );
this.callbackProcessor = new LegacyCallbackProcessor( jpaListenerFactory, configuration.getReflectionManager() );
Iterator classes = configuration.getClassMappings(); Iterator classes = configuration.getClassMappings();
ReflectionManager reflectionManager = configuration.getReflectionManager();
while ( classes.hasNext() ) { while ( classes.hasNext() ) {
PersistentClass clazz = (PersistentClass) classes.next(); final PersistentClass clazz = (PersistentClass) classes.next();
if ( clazz.getClassName() == null ) { if ( clazz.getClassName() == null ) {
//we can have non java class persisted by hibernate // we can have non java class persisted by hibernate
continue; continue;
} }
try { callbackProcessor.processCallbacksForEntity( clazz.getClassName(), callbackRegistry );
callbackHandler.add( reflectionManager.classForName( clazz.getClassName(), this.getClass() ), reflectionManager );
}
catch (ClassNotFoundException e) {
throw new MappingException( "entity class not found: " + clazz.getNodeName(), e );
}
} }
for ( EventType eventType : EventType.values() ) { for ( EventType eventType : EventType.values() ) {
final EventListenerGroup eventListenerGroup = eventListenerRegistry.getEventListenerGroup( eventType ); final EventListenerGroup eventListenerGroup = eventListenerRegistry.getEventListenerGroup( eventType );
for ( Object listener : eventListenerGroup.listeners() ) { for ( Object listener : eventListenerGroup.listeners() ) {
if ( CallbackHandlerConsumer.class.isInstance( listener ) ) { if ( CallbackRegistryConsumer.class.isInstance( listener ) ) {
( (CallbackHandlerConsumer) listener ).setCallbackHandler( callbackHandler ); ( (CallbackRegistryConsumer) listener ).injectCallbackRegistry( callbackRegistry );
} }
} }
} }
@ -258,34 +285,40 @@ public class JpaIntegrator implements Integrator {
} }
} }
final EntityCallbackHandler callbackHandler = new EntityCallbackHandler(); // handle JPA "entity listener classes"...
ClassLoaderService classLoaderSvc = serviceRegistry.getService(ClassLoaderService.class);
for (EntityBinding binding : metadata.getEntityBindings()) { this.callbackRegistry = new CallbackRegistry();
String name = binding.getEntity().getName(); // Should this be getClassName()? final BeanManager beanManager = (BeanManager) sessionFactory.getProperties().get( AvailableSettings.CDI_BEAN_MANAGER );
if (name == null) { this.jpaListenerFactory = beanManager == null
//we can have non java class persisted by hibernate ? new StandardListenerFactory()
continue; : new BeanManagerListenerFactory( beanManager );
} this.callbackProcessor = new CallbackProcessorImpl( jpaListenerFactory, metadata, serviceRegistry );
try {
callbackHandler.add(classLoaderSvc.classForName(name), classLoaderSvc, binding); for ( EntityBinding binding : metadata.getEntityBindings() ) {
} callbackProcessor.processCallbacksForEntity( binding, callbackRegistry );
catch (ClassLoadingException error) { }
throw new MappingException( "entity class not found: " + name, error );
for ( EventType eventType : EventType.values() ) {
final EventListenerGroup eventListenerGroup = eventListenerRegistry.getEventListenerGroup( eventType );
for ( Object listener : eventListenerGroup.listeners() ) {
if ( CallbackRegistryConsumer.class.isInstance( listener ) ) {
( (CallbackRegistryConsumer) listener ).injectCallbackRegistry( callbackRegistry );
}
} }
} }
//
// for ( EventType eventType : EventType.values() ) {
// final EventListenerGroup eventListenerGroup = eventListenerRegistry.getEventListenerGroup( eventType );
// for ( Object listener : eventListenerGroup.listeners() ) {
// if ( CallbackHandlerConsumer.class.isInstance( listener ) ) {
// ( (CallbackHandlerConsumer) listener ).setCallbackHandler( callbackHandler );
// }
// }
// }
} }
@Override @Override
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
if ( callbackRegistry != null ) {
callbackRegistry.release();
}
if ( callbackProcessor != null ) {
callbackProcessor.release();
}
if ( jpaListenerFactory != null ) {
jpaListenerFactory.release();
}
} }
private Object instantiate(String listenerImpl, ServiceRegistryImplementor serviceRegistry) { private Object instantiate(String listenerImpl, ServiceRegistryImplementor serviceRegistry) {

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.core;
/** /**
* Marker interface for handling listener duplication checking (to avoid multiple registrations). * Marker interface for handling listener duplication checking (to avoid multiple registrations).

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.core;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;

View File

@ -21,13 +21,15 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.core;
import java.io.Serializable; import java.io.Serializable;
import org.hibernate.event.internal.DefaultDeleteEventListener; import org.hibernate.event.internal.DefaultDeleteEventListener;
import org.hibernate.event.spi.DeleteEvent; import org.hibernate.event.spi.DeleteEvent;
import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.EventSource;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistryConsumer;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistry;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
/** /**
@ -35,25 +37,25 @@ import org.hibernate.persister.entity.EntityPersister;
* *
* @author Emmanuel Bernard * @author Emmanuel Bernard
*/ */
public class JpaDeleteEventListener extends DefaultDeleteEventListener implements CallbackHandlerConsumer { public class JpaDeleteEventListener extends DefaultDeleteEventListener implements CallbackRegistryConsumer {
private EntityCallbackHandler callbackHandler; private CallbackRegistry callbackRegistry;
public void setCallbackHandler(EntityCallbackHandler callbackHandler) { public void injectCallbackRegistry(CallbackRegistry callbackRegistry) {
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
public JpaDeleteEventListener() { public JpaDeleteEventListener() {
super(); super();
} }
public JpaDeleteEventListener(EntityCallbackHandler callbackHandler) { public JpaDeleteEventListener(CallbackRegistry callbackRegistry) {
this(); this();
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
@Override @Override
protected boolean invokeDeleteLifecycle(EventSource session, Object entity, EntityPersister persister) { protected boolean invokeDeleteLifecycle(EventSource session, Object entity, EntityPersister persister) {
callbackHandler.preRemove( entity ); callbackRegistry.preRemove( entity );
return super.invokeDeleteLifecycle( session, entity, persister ); return super.invokeDeleteLifecycle( session, entity, persister );
} }

View File

@ -21,13 +21,15 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.core;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.Status; import org.hibernate.engine.spi.Status;
import org.hibernate.event.internal.DefaultFlushEntityEventListener; import org.hibernate.event.internal.DefaultFlushEntityEventListener;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistryConsumer;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistry;
import org.hibernate.metadata.ClassMetadata; import org.hibernate.metadata.ClassMetadata;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.Type; import org.hibernate.type.Type;
@ -37,20 +39,20 @@ import org.hibernate.type.Type;
* *
* @author Emmanuel Bernard * @author Emmanuel Bernard
*/ */
public class JpaFlushEntityEventListener extends DefaultFlushEntityEventListener implements CallbackHandlerConsumer { public class JpaFlushEntityEventListener extends DefaultFlushEntityEventListener implements CallbackRegistryConsumer {
private EntityCallbackHandler callbackHandler; private CallbackRegistry callbackRegistry;
public void setCallbackHandler(EntityCallbackHandler callbackHandler) { public void injectCallbackRegistry(CallbackRegistry callbackRegistry) {
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
public JpaFlushEntityEventListener() { public JpaFlushEntityEventListener() {
super(); super();
} }
public JpaFlushEntityEventListener(EntityCallbackHandler callbackHandler) { public JpaFlushEntityEventListener(CallbackRegistry callbackRegistry) {
super(); super();
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
@Override @Override
@ -62,7 +64,7 @@ public class JpaFlushEntityEventListener extends DefaultFlushEntityEventListener
EntityPersister persister) { EntityPersister persister) {
boolean isDirty = false; boolean isDirty = false;
if ( entry.getStatus() != Status.DELETED ) { if ( entry.getStatus() != Status.DELETED ) {
if ( callbackHandler.preUpdate( entity ) ) { if ( callbackRegistry.preUpdate( entity ) ) {
isDirty = copyState( entity, persister.getPropertyTypes(), values, session.getFactory() ); isDirty = copyState( entity, persister.getPropertyTypes(), values, session.getFactory() );
} }
} }

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.core;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;

View File

@ -21,32 +21,34 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.core;
import java.io.Serializable; import java.io.Serializable;
import org.hibernate.event.internal.DefaultMergeEventListener; import org.hibernate.event.internal.DefaultMergeEventListener;
import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.EventSource;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistryConsumer;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistry;
/** /**
* Overrides the LifeCycle OnSave call to call the PrePersist operation * Overrides the LifeCycle OnSave call to call the PrePersist operation
* *
* @author Emmanuel Bernard * @author Emmanuel Bernard
*/ */
public class JpaMergeEventListener extends DefaultMergeEventListener implements CallbackHandlerConsumer { public class JpaMergeEventListener extends DefaultMergeEventListener implements CallbackRegistryConsumer {
private EntityCallbackHandler callbackHandler; private CallbackRegistry callbackRegistry;
public void setCallbackHandler(EntityCallbackHandler callbackHandler) { public void injectCallbackRegistry(CallbackRegistry callbackRegistry) {
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
public JpaMergeEventListener() { public JpaMergeEventListener() {
super(); super();
} }
public JpaMergeEventListener(EntityCallbackHandler callbackHandler) { public JpaMergeEventListener(CallbackRegistry callbackRegistry) {
super(); super();
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
@Override @Override
@ -56,7 +58,7 @@ public class JpaMergeEventListener extends DefaultMergeEventListener implements
String entityName, String entityName,
Object anything, Object anything,
EventSource source) { EventSource source) {
callbackHandler.preCreate( entity ); callbackRegistry.preCreate( entity );
return super.saveWithRequestedId( entity, requestedId, entityName, anything, source ); return super.saveWithRequestedId( entity, requestedId, entityName, anything, source );
} }
@ -67,7 +69,7 @@ public class JpaMergeEventListener extends DefaultMergeEventListener implements
Object anything, Object anything,
EventSource source, EventSource source,
boolean requiresImmediateIdAccess) { boolean requiresImmediateIdAccess) {
callbackHandler.preCreate( entity ); callbackRegistry.preCreate( entity );
return super.saveWithGeneratedId( entity, entityName, anything, source, requiresImmediateIdAccess ); return super.saveWithGeneratedId( entity, entityName, anything, source, requiresImmediateIdAccess );
} }
} }

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.core;
import java.io.Serializable; import java.io.Serializable;
import java.util.Iterator; import java.util.Iterator;
@ -34,6 +34,8 @@ import org.hibernate.engine.spi.CascadingAction;
import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.event.internal.DefaultPersistEventListener; import org.hibernate.event.internal.DefaultPersistEventListener;
import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.EventSource;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistryConsumer;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistry;
import org.hibernate.type.CollectionType; import org.hibernate.type.CollectionType;
/** /**
@ -41,23 +43,23 @@ import org.hibernate.type.CollectionType;
* *
* @author Emmanuel Bernard * @author Emmanuel Bernard
*/ */
public class JpaPersistEventListener extends DefaultPersistEventListener implements CallbackHandlerConsumer { public class JpaPersistEventListener extends DefaultPersistEventListener implements CallbackRegistryConsumer {
private static final Logger log = Logger.getLogger( JpaPersistEventListener.class ); private static final Logger log = Logger.getLogger( JpaPersistEventListener.class );
private EntityCallbackHandler callbackHandler; private CallbackRegistry callbackRegistry;
@Override @Override
public void setCallbackHandler(EntityCallbackHandler callbackHandler) { public void injectCallbackRegistry(CallbackRegistry callbackRegistry) {
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
public JpaPersistEventListener() { public JpaPersistEventListener() {
super(); super();
} }
public JpaPersistEventListener(EntityCallbackHandler callbackHandler) { public JpaPersistEventListener(CallbackRegistry callbackRegistry) {
super(); super();
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
@Override @Override
@ -67,7 +69,7 @@ public class JpaPersistEventListener extends DefaultPersistEventListener impleme
String entityName, String entityName,
Object anything, Object anything,
EventSource source) { EventSource source) {
callbackHandler.preCreate( entity ); callbackRegistry.preCreate( entity );
return super.saveWithRequestedId( entity, requestedId, entityName, anything, source ); return super.saveWithRequestedId( entity, requestedId, entityName, anything, source );
} }
@ -78,7 +80,7 @@ public class JpaPersistEventListener extends DefaultPersistEventListener impleme
Object anything, Object anything,
EventSource source, EventSource source,
boolean requiresImmediateIdAccess) { boolean requiresImmediateIdAccess) {
callbackHandler.preCreate( entity ); callbackRegistry.preCreate( entity );
return super.saveWithGeneratedId( entity, entityName, anything, source, requiresImmediateIdAccess ); return super.saveWithGeneratedId( entity, entityName, anything, source, requiresImmediateIdAccess );
} }

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.core;
import org.hibernate.engine.spi.CascadingAction; import org.hibernate.engine.spi.CascadingAction;
import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.CascadingActions;

View File

@ -21,32 +21,34 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.core;
import org.hibernate.event.spi.PostDeleteEvent; import org.hibernate.event.spi.PostDeleteEvent;
import org.hibernate.event.spi.PostDeleteEventListener; import org.hibernate.event.spi.PostDeleteEventListener;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistryConsumer;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistry;
/** /**
* @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a> * @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
*/ */
public class JpaPostDeleteEventListener implements PostDeleteEventListener, CallbackHandlerConsumer { public class JpaPostDeleteEventListener implements PostDeleteEventListener, CallbackRegistryConsumer {
EntityCallbackHandler callbackHandler; CallbackRegistry callbackRegistry;
public void setCallbackHandler(EntityCallbackHandler callbackHandler) { public void injectCallbackRegistry(CallbackRegistry callbackRegistry) {
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
public JpaPostDeleteEventListener() { public JpaPostDeleteEventListener() {
super(); super();
} }
public JpaPostDeleteEventListener(EntityCallbackHandler callbackHandler) { public JpaPostDeleteEventListener(CallbackRegistry callbackRegistry) {
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
public void onPostDelete(PostDeleteEvent event) { public void onPostDelete(PostDeleteEvent event) {
Object entity = event.getEntity(); Object entity = event.getEntity();
callbackHandler.postRemove( entity ); callbackRegistry.postRemove( entity );
} }
} }

View File

@ -21,33 +21,35 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.core;
import org.hibernate.event.spi.PostInsertEvent; import org.hibernate.event.spi.PostInsertEvent;
import org.hibernate.event.spi.PostInsertEventListener; import org.hibernate.event.spi.PostInsertEventListener;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistryConsumer;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistry;
/** /**
* @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a> * @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
*/ */
public class JpaPostInsertEventListener implements PostInsertEventListener, CallbackHandlerConsumer { public class JpaPostInsertEventListener implements PostInsertEventListener, CallbackRegistryConsumer {
EntityCallbackHandler callbackHandler; CallbackRegistry callbackRegistry;
@Override @Override
public void setCallbackHandler(EntityCallbackHandler callbackHandler) { public void injectCallbackRegistry(CallbackRegistry callbackRegistry) {
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
public JpaPostInsertEventListener() { public JpaPostInsertEventListener() {
super(); super();
} }
public JpaPostInsertEventListener(EntityCallbackHandler callbackHandler) { public JpaPostInsertEventListener(CallbackRegistry callbackRegistry) {
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
@Override @Override
public void onPostInsert(PostInsertEvent event) { public void onPostInsert(PostInsertEvent event) {
Object entity = event.getEntity(); Object entity = event.getEntity();
callbackHandler.postCreate( entity ); callbackRegistry.postCreate( entity );
} }
} }

View File

@ -21,34 +21,36 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.core;
import org.hibernate.event.spi.PostLoadEvent; import org.hibernate.event.spi.PostLoadEvent;
import org.hibernate.event.spi.PostLoadEventListener; import org.hibernate.event.spi.PostLoadEventListener;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistryConsumer;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistry;
/** /**
* @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a> * @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
*/ */
public class JpaPostLoadEventListener implements PostLoadEventListener, CallbackHandlerConsumer { public class JpaPostLoadEventListener implements PostLoadEventListener, CallbackRegistryConsumer {
EntityCallbackHandler callbackHandler; CallbackRegistry callbackRegistry;
@Override @Override
public void setCallbackHandler(EntityCallbackHandler callbackHandler) { public void injectCallbackRegistry(CallbackRegistry callbackRegistry) {
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
public JpaPostLoadEventListener() { public JpaPostLoadEventListener() {
super(); super();
} }
public JpaPostLoadEventListener(EntityCallbackHandler callbackHandler) { public JpaPostLoadEventListener(CallbackRegistry callbackRegistry) {
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
@Override @Override
public void onPostLoad(PostLoadEvent event) { public void onPostLoad(PostLoadEvent event) {
Object entity = event.getEntity(); Object entity = event.getEntity();
callbackHandler.postLoad( entity ); callbackRegistry.postLoad( entity );
} }
} }

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.core;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.Status; import org.hibernate.engine.spi.Status;
@ -34,6 +34,8 @@ import org.hibernate.event.spi.PostCollectionUpdateEvent;
import org.hibernate.event.spi.PostCollectionUpdateEventListener; import org.hibernate.event.spi.PostCollectionUpdateEventListener;
import org.hibernate.event.spi.PostUpdateEvent; import org.hibernate.event.spi.PostUpdateEvent;
import org.hibernate.event.spi.PostUpdateEventListener; import org.hibernate.event.spi.PostUpdateEventListener;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistryConsumer;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistry;
/** /**
* Implementation of the post update listeners. * Implementation of the post update listeners.
@ -43,23 +45,23 @@ import org.hibernate.event.spi.PostUpdateEventListener;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class JpaPostUpdateEventListener public class JpaPostUpdateEventListener
implements PostUpdateEventListener, implements PostUpdateEventListener,
CallbackHandlerConsumer, CallbackRegistryConsumer,
PostCollectionRecreateEventListener, PostCollectionRecreateEventListener,
PostCollectionRemoveEventListener, PostCollectionRemoveEventListener,
PostCollectionUpdateEventListener { PostCollectionUpdateEventListener {
EntityCallbackHandler callbackHandler; CallbackRegistry callbackRegistry;
@Override @Override
public void setCallbackHandler(EntityCallbackHandler callbackHandler) { public void injectCallbackRegistry(CallbackRegistry callbackRegistry) {
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
public JpaPostUpdateEventListener() { public JpaPostUpdateEventListener() {
super(); super();
} }
public JpaPostUpdateEventListener(EntityCallbackHandler callbackHandler) { public JpaPostUpdateEventListener(CallbackRegistry callbackRegistry) {
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
@Override @Override
@ -74,7 +76,7 @@ public class JpaPostUpdateEventListener
.getEntityEntries().get(entity); .getEntityEntries().get(entity);
// mimic the preUpdate filter // mimic the preUpdate filter
if ( Status.DELETED != entry.getStatus()) { if ( Status.DELETED != entry.getStatus()) {
callbackHandler.postUpdate(entity); callbackRegistry.postUpdate(entity);
} }
} }

View File

@ -21,32 +21,34 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.core;
import java.io.Serializable; import java.io.Serializable;
import org.hibernate.event.internal.DefaultSaveEventListener; import org.hibernate.event.internal.DefaultSaveEventListener;
import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.EventSource;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistryConsumer;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistry;
/** /**
* Overrides the LifeCycle OnSave call to call the PrePersist operation * Overrides the LifeCycle OnSave call to call the PrePersist operation
* *
* @author Emmanuel Bernard * @author Emmanuel Bernard
*/ */
public class JpaSaveEventListener extends DefaultSaveEventListener implements CallbackHandlerConsumer { public class JpaSaveEventListener extends DefaultSaveEventListener implements CallbackRegistryConsumer {
private EntityCallbackHandler callbackHandler; private CallbackRegistry callbackRegistry;
public void setCallbackHandler(EntityCallbackHandler callbackHandler) { public void injectCallbackRegistry(CallbackRegistry callbackRegistry) {
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
public JpaSaveEventListener() { public JpaSaveEventListener() {
super(); super();
} }
public JpaSaveEventListener(EntityCallbackHandler callbackHandler) { public JpaSaveEventListener(CallbackRegistry callbackRegistry) {
super(); super();
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
@Override @Override
@ -56,7 +58,7 @@ public class JpaSaveEventListener extends DefaultSaveEventListener implements Ca
String entityName, String entityName,
Object anything, Object anything,
EventSource source) { EventSource source) {
callbackHandler.preCreate( entity ); callbackRegistry.preCreate( entity );
return super.saveWithRequestedId( entity, requestedId, entityName, anything, source ); return super.saveWithRequestedId( entity, requestedId, entityName, anything, source );
} }
@ -67,7 +69,7 @@ public class JpaSaveEventListener extends DefaultSaveEventListener implements Ca
Object anything, Object anything,
EventSource source, EventSource source,
boolean requiresImmediateIdAccess) { boolean requiresImmediateIdAccess) {
callbackHandler.preCreate( entity ); callbackRegistry.preCreate( entity );
return super.saveWithGeneratedId( entity, entityName, anything, source, requiresImmediateIdAccess ); return super.saveWithGeneratedId( entity, entityName, anything, source, requiresImmediateIdAccess );
} }
} }

View File

@ -21,32 +21,34 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.core;
import java.io.Serializable; import java.io.Serializable;
import org.hibernate.event.internal.DefaultSaveOrUpdateEventListener; import org.hibernate.event.internal.DefaultSaveOrUpdateEventListener;
import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.EventSource;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistryConsumer;
import org.hibernate.jpa.internal.event.jpa.CallbackRegistry;
/** /**
* Overrides the LifeCycle OnSave call to call the PrePersist operation * Overrides the LifeCycle OnSave call to call the PrePersist operation
* *
* @author Emmanuel Bernard * @author Emmanuel Bernard
*/ */
public class JpaSaveOrUpdateEventListener extends DefaultSaveOrUpdateEventListener implements CallbackHandlerConsumer { public class JpaSaveOrUpdateEventListener extends DefaultSaveOrUpdateEventListener implements CallbackRegistryConsumer {
private EntityCallbackHandler callbackHandler; private CallbackRegistry callbackRegistry;
public void setCallbackHandler(EntityCallbackHandler callbackHandler) { public void injectCallbackRegistry(CallbackRegistry callbackRegistry) {
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
public JpaSaveOrUpdateEventListener() { public JpaSaveOrUpdateEventListener() {
super(); super();
} }
public JpaSaveOrUpdateEventListener(EntityCallbackHandler callbackHandler) { public JpaSaveOrUpdateEventListener(CallbackRegistry callbackRegistry) {
super(); super();
this.callbackHandler = callbackHandler; this.callbackRegistry = callbackRegistry;
} }
@Override @Override
@ -56,7 +58,7 @@ public class JpaSaveOrUpdateEventListener extends DefaultSaveOrUpdateEventListen
String entityName, String entityName,
Object anything, Object anything,
EventSource source) { EventSource source) {
callbackHandler.preCreate( entity ); callbackRegistry.preCreate( entity );
return super.saveWithRequestedId( entity, requestedId, entityName, anything, source ); return super.saveWithRequestedId( entity, requestedId, entityName, anything, source );
} }
@ -67,7 +69,7 @@ public class JpaSaveOrUpdateEventListener extends DefaultSaveOrUpdateEventListen
Object anything, Object anything,
EventSource source, EventSource source,
boolean requiresImmediateIdAccess) { boolean requiresImmediateIdAccess) {
callbackHandler.preCreate( entity ); callbackRegistry.preCreate( entity );
return super.saveWithGeneratedId( entity, entityName, anything, source, requiresImmediateIdAccess ); return super.saveWithGeneratedId( entity, entityName, anything, source, requiresImmediateIdAccess );
} }
} }

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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
*/
package org.hibernate.jpa.internal.event.core;
/**
* Hibernate EntityManager specific implementations of Hibernate event listeners. Generally the listeners
* here either:<ul>
* <li>provide tweaks to internal processing to conform with JPA spec</li>
* <li>bridge to JPA event callbacks</li>
* </ul>
*/

View File

@ -0,0 +1,87 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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
*/
package org.hibernate.jpa.internal.event.jpa;
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 java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 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<Class,BeanMetaData> listeners = new ConcurrentHashMap<Class, BeanMetaData>();
public BeanManagerListenerFactory(BeanManager beanManager) {
this.beanManager = beanManager;
}
@Override
public <T> T buildListener(Class<T> listenerClass) {
BeanMetaData<T> beanMetaData = listeners.get( listenerClass );
if ( beanMetaData == null ) {
beanMetaData = new BeanMetaData<T>( listenerClass );
listeners.put( listenerClass, beanMetaData );
}
return beanMetaData.instance;
}
@Override
public void release() {
for ( BeanMetaData beanMetaData : listeners.values() ) {
beanMetaData.release();
}
listeners.clear();
}
private class BeanMetaData<T> {
private final InjectionTarget<T> injectionTarget;
private final CreationalContext<T> creationalContext;
private final T instance;
private BeanMetaData(Class<T> listenerClass) {
AnnotatedType<T> 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();
}
}
}

View File

@ -0,0 +1,41 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009-2011, 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
*/
package org.hibernate.jpa.internal.event.jpa;
import java.io.Serializable;
/**
* Represents a JPA event callback.
*
* @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
* @author Steve Ebersole
*/
public interface Callback extends Serializable {
/**
* Contract for performing the callback
*
* @param entity Reference to the entity for which the callback is triggered.
*/
public abstract void performCallback(Object entity);
}

View File

@ -0,0 +1,58 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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
*/
package org.hibernate.jpa.internal.event.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, CallbackRegistry registry);
public void release();
}

View File

@ -0,0 +1,162 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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
*/
package org.hibernate.jpa.internal.event.jpa;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.jboss.logging.Logger;
import org.hibernate.MappingException;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.metamodel.source.binder.JpaCallbackClass;
import org.hibernate.service.classloading.spi.ClassLoaderService;
import org.hibernate.service.classloading.spi.ClassLoadingException;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
/**
* @author Steve Ebersole
*/
public class CallbackProcessorImpl implements CallbackProcessor {
private static final Logger log = Logger.getLogger( CallbackProcessorImpl.class );
private final ListenerFactory jpaListenerFactory;
private final MetadataImplementor metadata;
private final ClassLoaderService classLoaderService;
public CallbackProcessorImpl(
ListenerFactory jpaListenerFactory,
MetadataImplementor metadata,
SessionFactoryServiceRegistry serviceRegistry) {
this.jpaListenerFactory = jpaListenerFactory;
this.metadata = metadata;
this.classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
}
@Override
public void processCallbacksForEntity(Object entityObject, CallbackRegistry callbackRegistry) {
final EntityBinding entityBinding = (EntityBinding) entityObject;
final String entityClassName = entityBinding.getEntity().getClassName();
if ( entityClassName == null ) {
return;
}
try {
final Class entityClass = classLoaderService.classForName( entityClassName );
for ( Class annotationClass : CALLBACK_ANNOTATION_CLASSES ) {
callbackRegistry.addEntityCallbacks(
entityClass,
annotationClass,
collectCallbacks( entityBinding, entityClass, annotationClass )
);
}
}
catch (ClassLoadingException e) {
throw new MappingException( "entity class not found: " + entityClassName, e );
}
}
private Callback[] collectCallbacks(EntityBinding entityBinding, Class entityClass, Class annotationClass) {
final List<Callback> callbacks = new ArrayList<Callback>();
for ( JpaCallbackClass jpaCallbackClass : entityBinding.getJpaCallbackClasses() ) {
final Class listenerClass = classLoaderService.classForName( jpaCallbackClass.getName() );
final String methodName = jpaCallbackClass.getCallbackMethod( annotationClass );
log.debugf(
"Adding $s.%s as %s callback for entity %s",
listenerClass.getName(),
methodName,
annotationClass.getName(),
entityClass.getName()
);
final Callback callback = jpaCallbackClass.isListener()
? createListenerCallback( listenerClass, entityClass, methodName )
: createBeanCallback( listenerClass, methodName );
assert callback != null;
callbacks.add(callback);
}
return callbacks.toArray(new Callback[callbacks.size()]);
}
private Callback createListenerCallback(
Class listenerClass,
Class entityClass,
String methodName ) {
final Class<?> callbackSuperclass = listenerClass.getSuperclass();
if ( callbackSuperclass != null ) {
Callback callback = createListenerCallback( entityClass, callbackSuperclass, methodName );
if ( callback != null ) {
return callback;
}
}
final Object listenerInstance = jpaListenerFactory.buildListener( listenerClass );
for ( Method method : listenerClass.getDeclaredMethods() ) {
if ( !method.getName().equals(methodName) ) {
continue;
}
final Class<?>[] argTypes = method.getParameterTypes();
if (argTypes.length != 1) {
continue;
}
final Class<?> argType = argTypes[0];
if (argType != Object.class && argType != entityClass) {
continue;
}
if (!method.isAccessible()) {
method.setAccessible( true );
}
return new ListenerCallback( listenerInstance, method );
}
return null;
}
private Callback createBeanCallback( Class<?> callbackClass,
String methodName ) {
Class<?> callbackSuperclass = callbackClass.getSuperclass();
if (callbackSuperclass != null) {
Callback callback = createBeanCallback(callbackSuperclass, methodName);
if (callback != null) return callback;
}
for (Method method : callbackClass.getDeclaredMethods()) {
if (!method.getName().equals(methodName)) continue;
if (method.getParameterTypes().length != 0) continue;
if (!method.isAccessible()) method.setAccessible(true);
return new EntityCallback(method);
}
return null;
}
@Override
public void release() {
//To change body of implemented methods use File | Settings | File Templates.
}
}

View File

@ -21,10 +21,9 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.jpa;
import java.io.Serializable; import javax.persistence.PersistenceException;
import java.util.HashMap;
import javax.persistence.PostLoad; import javax.persistence.PostLoad;
import javax.persistence.PostPersist; import javax.persistence.PostPersist;
import javax.persistence.PostRemove; import javax.persistence.PostRemove;
@ -32,19 +31,17 @@ import javax.persistence.PostUpdate;
import javax.persistence.PrePersist; import javax.persistence.PrePersist;
import javax.persistence.PreRemove; import javax.persistence.PreRemove;
import javax.persistence.PreUpdate; import javax.persistence.PreUpdate;
import java.io.Serializable;
import org.hibernate.annotations.common.reflection.ReflectionManager; import java.util.HashMap;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.service.classloading.spi.ClassLoaderService;
/** /**
* Keep track of all lifecycle callbacks and listeners for a given persistence unit * Keep track of all lifecycle callbacks and listeners for a given persistence unit
* *
* @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a> * @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
* @author Steve Ebersole
*/ */
@SuppressWarnings({"unchecked", "serial"}) @SuppressWarnings({"unchecked", "serial"})
public class EntityCallbackHandler implements Serializable { public class CallbackRegistry implements Serializable {
private HashMap<Class, Callback[]> preCreates = new HashMap<Class, Callback[]>(); private HashMap<Class, Callback[]> preCreates = new HashMap<Class, Callback[]>();
private HashMap<Class, Callback[]> postCreates = new HashMap<Class, Callback[]>(); private HashMap<Class, Callback[]> postCreates = new HashMap<Class, Callback[]>();
private HashMap<Class, Callback[]> preRemoves = new HashMap<Class, Callback[]>(); private HashMap<Class, Callback[]> preRemoves = new HashMap<Class, Callback[]>();
@ -53,28 +50,6 @@ public class EntityCallbackHandler implements Serializable {
private HashMap<Class, Callback[]> postUpdates = new HashMap<Class, Callback[]>(); private HashMap<Class, Callback[]> postUpdates = new HashMap<Class, Callback[]>();
private HashMap<Class, Callback[]> postLoads = new HashMap<Class, Callback[]>(); private HashMap<Class, Callback[]> postLoads = new HashMap<Class, Callback[]>();
public void add(XClass entity, ReflectionManager reflectionManager) {
addCallback( entity, preCreates, PrePersist.class, reflectionManager );
addCallback( entity, postCreates, PostPersist.class, reflectionManager );
addCallback( entity, preRemoves, PreRemove.class, reflectionManager );
addCallback( entity, postRemoves, PostRemove.class, reflectionManager );
addCallback( entity, preUpdates, PreUpdate.class, reflectionManager );
addCallback( entity, postUpdates, PostUpdate.class, reflectionManager );
addCallback( entity, postLoads, PostLoad.class, reflectionManager );
}
public void add( Class entity,
ClassLoaderService classLoaderService,
EntityBinding binding ) {
addCallback( entity, preCreates, PrePersist.class, classLoaderService, binding );
addCallback( entity, postCreates, PostPersist.class, classLoaderService, binding );
addCallback( entity, preRemoves, PreRemove.class, classLoaderService, binding );
addCallback( entity, postRemoves, PostRemove.class, classLoaderService, binding );
addCallback( entity, preUpdates, PreUpdate.class, classLoaderService, binding );
addCallback( entity, postUpdates, PostUpdate.class, classLoaderService, binding );
addCallback( entity, postLoads, PostLoad.class, classLoaderService, binding );
}
public boolean preCreate(Object bean) { public boolean preCreate(Object bean) {
return callback( preCreates.get( bean.getClass() ), bean ); return callback( preCreates.get( bean.getClass() ), bean );
} }
@ -103,11 +78,10 @@ public class EntityCallbackHandler implements Serializable {
return callback( postLoads.get( bean.getClass() ), bean ); return callback( postLoads.get( bean.getClass() ), bean );
} }
private boolean callback(Callback[] callbacks, Object bean) { private boolean callback(Callback[] callbacks, Object bean) {
if ( callbacks != null && callbacks.length != 0 ) { if ( callbacks != null && callbacks.length != 0 ) {
for ( Callback callback : callbacks ) { for ( Callback callback : callbacks ) {
callback.invoke( bean ); callback.performCallback( bean );
} }
return true; return true;
} }
@ -116,19 +90,56 @@ public class EntityCallbackHandler implements Serializable {
} }
} }
private void addCallback( /* package */ void addEntityCallbacks(Class entityClass, Class annotationClass, Callback[] callbacks) {
XClass entity, HashMap<Class, Callback[]> map, Class annotation, ReflectionManager reflectionManager final HashMap<Class, Callback[]> map = determineAppropriateCallbackMap( annotationClass );
) { if ( map.containsKey( entityClass ) ) {
Callback[] callbacks = null; throw new PersistenceException( "Error build callback listeners; entity [" + entityClass.getName() + " was already processed" );
callbacks = CallbackResolver.resolveCallback( entity, annotation, reflectionManager ); }
map.put( reflectionManager.toClass( entity ), callbacks ); map.put( entityClass, callbacks );
} }
private void addCallback( Class<?> entity, private HashMap<Class, Callback[]> determineAppropriateCallbackMap(Class annotationClass) {
HashMap<Class, Callback[]> map, if ( PrePersist.class.equals( annotationClass ) ) {
Class annotation, return preCreates;
ClassLoaderService classLoaderService, }
EntityBinding binding ) {
map.put(entity, CallbackResolver.resolveCallbacks(entity, annotation, classLoaderService, binding)); if ( PostPersist.class.equals( annotationClass ) ) {
} return postCreates;
}
if ( PreRemove.class.equals( annotationClass ) ) {
return preRemoves;
}
if ( PostRemove.class.equals( annotationClass ) ) {
return postRemoves;
}
if ( PreUpdate.class.equals( annotationClass ) ) {
return preUpdates;
}
if ( PostUpdate.class.equals( annotationClass ) ) {
return postUpdates;
}
if ( PostLoad.class.equals( annotationClass ) ) {
return postLoads;
}
throw new PersistenceException( "Unrecognized JPA callback annotation [" + annotationClass.getName() + "]" );
}
public void release() {
preCreates.clear();
postCreates.clear();
preRemoves.clear();
postRemoves.clear();
preUpdates.clear();
postUpdates.clear();
postLoads.clear();
}
} }

View File

@ -0,0 +1,41 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009-2011, 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
*/
package org.hibernate.jpa.internal.event.jpa;
import org.hibernate.jpa.internal.event.core.HibernateEntityManagerEventListener;
/**
* Contract for injecting the registry of Callbacks into event listeners.
*
* @author Emmanuel Bernard
* @author Steve Ebersole
*/
public interface CallbackRegistryConsumer extends HibernateEntityManagerEventListener {
/**
* Injection of the CallbackRegistry
*
* @param callbackRegistry The CallbackRegistry
*/
public void injectCallbackRegistry(CallbackRegistry callbackRegistry);
}

View File

@ -21,22 +21,28 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.jpa;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
/** /**
* Represents a JPA callback on the entity itself
*
* @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a> * @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
* @author Steve Ebersole
*/ */
public class BeanCallback extends Callback { public class EntityCallback implements Callback {
public BeanCallback(Method callbackMethod) { private Method callbackMethod;
super( callbackMethod );
public EntityCallback(Method callbackMethod) {
this.callbackMethod = callbackMethod;
} }
public void invoke(Object bean) { @Override
public void performCallback(Object entity) {
try { try {
callbackMethod.invoke( bean, new Object[0] ); callbackMethod.invoke( entity );
} }
catch (InvocationTargetException e) { catch (InvocationTargetException e) {
//keep runtime exceptions as is //keep runtime exceptions as is

View File

@ -1,7 +1,7 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2009-2011, Red Hat Inc. or third-party contributors as * Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc. * distributed under license by Red Hat Inc.
@ -21,7 +21,21 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.jpa;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.ExcludeDefaultListeners;
import javax.persistence.ExcludeSuperclassListeners;
import javax.persistence.MappedSuperclass;
import javax.persistence.PersistenceException;
import 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 java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
@ -29,48 +43,46 @@ import java.lang.annotation.Target;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.ExcludeDefaultListeners;
import javax.persistence.ExcludeSuperclassListeners;
import javax.persistence.MappedSuperclass;
import javax.persistence.PersistenceException;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XMethod; import org.hibernate.annotations.common.reflection.XMethod;
import org.hibernate.jpa.internal.EntityManagerMessageLogger;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.source.binder.JpaCallbackClass;
import org.hibernate.service.classloading.spi.ClassLoaderService;
/** /**
* @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a> * @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
* @author Steve Ebersole
*/ */
public final class CallbackResolver { public class LegacyCallbackProcessor implements CallbackProcessor {
private static final Logger log = Logger.getLogger( LegacyCallbackProcessor.class );
private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(EntityManagerMessageLogger.class, private final ListenerFactory jpaListenerFactory;
CallbackResolver.class.getName()); private final ReflectionManager reflectionManager;
private static boolean useAnnotationAnnotatedByListener; public LegacyCallbackProcessor(ListenerFactory jpaListenerFactory, ReflectionManager reflectionManager) {
this.jpaListenerFactory = jpaListenerFactory;
this.reflectionManager = reflectionManager;
}
static { @Override
//check whether reading annotations of annotations is useful or not public void processCallbacksForEntity(Object entityObject, CallbackRegistry callbackRegistry) {
useAnnotationAnnotatedByListener = false; final String entityClassName = (String) entityObject;
Target target = EntityListeners.class.getAnnotation( Target.class ); try {
if ( target != null ) { final XClass entityXClass = reflectionManager.classForName( entityClassName, this.getClass() );
for ( ElementType type : target.value() ) { final Class entityClass = reflectionManager.toClass( entityXClass );
if ( type.equals( ElementType.ANNOTATION_TYPE ) ) useAnnotationAnnotatedByListener = true; for ( Class annotationClass : CALLBACK_ANNOTATION_CLASSES ) {
final Callback[] callbacks = resolveCallbacks( entityXClass, annotationClass, reflectionManager );
callbackRegistry.addEntityCallbacks( entityClass, annotationClass, callbacks );
} }
} }
catch (ClassNotFoundException e) {
throw new MappingException( "entity class not found: " + entityClassName, e );
}
} }
private CallbackResolver() { public Callback[] resolveCallbacks(XClass beanClass, Class annotation, ReflectionManager reflectionManager) {
}
public static Callback[] resolveCallback(XClass beanClass, Class annotation, ReflectionManager reflectionManager) {
List<Callback> callbacks = new ArrayList<Callback>(); List<Callback> callbacks = new ArrayList<Callback>();
List<String> callbacksMethodNames = new ArrayList<String>(); //used to track overridden methods List<String> callbacksMethodNames = new ArrayList<String>(); //used to track overridden methods
List<Class> orderedListeners = new ArrayList<Class>(); List<Class> orderedListeners = new ArrayList<Class>();
@ -89,7 +101,7 @@ public final class CallbackResolver {
if ( ! callbacksMethodNames.contains( methodName ) ) { if ( ! callbacksMethodNames.contains( methodName ) ) {
//overridden method, remove the superclass overridden method //overridden method, remove the superclass overridden method
if ( callback == null ) { if ( callback == null ) {
callback = new BeanCallback( method ); callback = new EntityCallback( method );
Class returnType = method.getReturnType(); Class returnType = method.getReturnType();
Class[] args = method.getParameterTypes(); Class[] args = method.getParameterTypes();
if ( returnType != Void.TYPE || args.length != 0 ) { if ( returnType != Void.TYPE || args.length != 0 ) {
@ -98,11 +110,11 @@ public final class CallbackResolver {
.getName() + " - " + xMethod .getName() + " - " + xMethod
); );
} }
if (!method.isAccessible()) method.setAccessible(true); if (!method.isAccessible()) method.setAccessible(true);
LOG.debugf("Adding %s as %s callback for entity %s", log.debugf("Adding %s as %s callback for entity %s",
methodName, methodName,
annotation.getSimpleName(), annotation.getSimpleName(),
beanClass.getName()); beanClass.getName());
callbacks.add( 0, callback ); //superclass first callbacks.add( 0, callback ); //superclass first
callbacksMethodNames.add( 0, methodName ); callbacksMethodNames.add( 0, methodName );
} }
@ -158,21 +170,8 @@ public final class CallbackResolver {
if ( ! callbacksMethodNames.contains( methodName ) ) { if ( ! callbacksMethodNames.contains( methodName ) ) {
//overridden method, remove the superclass overridden method //overridden method, remove the superclass overridden method
if ( callback == null ) { if ( callback == null ) {
try { callback = new ListenerCallback( jpaListenerFactory.buildListener( listener ), method );
callback = new ListenerCallback( method, listener.newInstance() );
}
catch (IllegalAccessException e) {
throw new PersistenceException(
"Unable to create instance of " + listener.getName()
+ " as a listener of beanClass", e
);
}
catch (InstantiationException e) {
throw new PersistenceException(
"Unable to create instance of " + listener.getName()
+ " as a listener of beanClass", e
);
}
Class returnType = method.getReturnType(); Class returnType = method.getReturnType();
Class[] args = method.getParameterTypes(); Class[] args = method.getParameterTypes();
if ( returnType != Void.TYPE || args.length != 1 ) { if ( returnType != Void.TYPE || args.length != 1 ) {
@ -181,11 +180,11 @@ public final class CallbackResolver {
.getName() + " - " + method .getName() + " - " + method
); );
} }
if (!method.isAccessible()) method.setAccessible(true); if (!method.isAccessible()) method.setAccessible(true);
LOG.debugf("Adding %s as %s callback for entity %s", log.debugf("Adding %s as %s callback for entity %s",
methodName, methodName,
annotation.getSimpleName(), annotation.getSimpleName(),
beanClass.getName()); beanClass.getName());
callbacks.add( 0, callback ); // listeners first callbacks.add( 0, callback ); // listeners first
} }
else { else {
@ -203,61 +202,18 @@ public final class CallbackResolver {
return callbacks.toArray( new Callback[ callbacks.size() ] ); return callbacks.toArray( new Callback[ callbacks.size() ] );
} }
public static Callback[] resolveCallbacks( Class<?> entityClass, private static boolean useAnnotationAnnotatedByListener;
Class<?> callbackClass,
ClassLoaderService classLoaderService,
EntityBinding binding ) {
List<Callback> callbacks = new ArrayList<Callback>();
for (JpaCallbackClass jpaCallbackClass : binding.getJpaCallbackClasses()) {
Object listener = classLoaderService.classForName(jpaCallbackClass.getName());
String methodName = jpaCallbackClass.getCallbackMethod( callbackClass );
Callback callback = jpaCallbackClass.isListener() ?
createListenerCallback(entityClass, callbackClass, listener, methodName) :
createBeanCallback(callbackClass, methodName);
LOG.debugf("Adding %s as %s callback for entity %s", methodName, callbackClass.getName(),
entityClass.getName());
assert callback != null;
callbacks.add(callback);
}
return callbacks.toArray(new Callback[callbacks.size()]);
}
private static Callback createListenerCallback( Class<?> entityClass, static {
Class<?> callbackClass, //check whether reading annotations of annotations is useful or not
Object listener, useAnnotationAnnotatedByListener = false;
String methodName ) { Target target = EntityListeners.class.getAnnotation( Target.class );
Class<?> callbackSuperclass = callbackClass.getSuperclass(); if ( target != null ) {
if (callbackSuperclass != null) { for ( ElementType type : target.value() ) {
Callback callback = createListenerCallback(entityClass, callbackSuperclass, listener, methodName); if ( type.equals( ElementType.ANNOTATION_TYPE ) ) useAnnotationAnnotatedByListener = true;
if (callback != null) return callback; }
} }
for (Method method : callbackClass.getDeclaredMethods()) { }
if (!method.getName().equals(methodName)) continue;
Class<?>[] argTypes = method.getParameterTypes();
if (argTypes.length != 1) continue;
Class<?> argType = argTypes[0];
if (argType != Object.class && argType != entityClass) continue;
if (!method.isAccessible()) method.setAccessible(true);
return new ListenerCallback(method, listener);
}
return null;
}
private static Callback createBeanCallback( Class<?> callbackClass,
String methodName ) {
Class<?> callbackSuperclass = callbackClass.getSuperclass();
if (callbackSuperclass != null) {
Callback callback = createBeanCallback(callbackSuperclass, methodName);
if (callback != null) return callback;
}
for (Method method : callbackClass.getDeclaredMethods()) {
if (!method.getName().equals(methodName)) continue;
if (method.getParameterTypes().length != 0) continue;
if (!method.isAccessible()) method.setAccessible(true);
return new BeanCallback(method);
}
return null;
}
private static void getListeners(XClass currentClazz, List<Class> orderedListeners) { private static void getListeners(XClass currentClazz, List<Class> orderedListeners) {
EntityListeners entityListeners = currentClazz.getAnnotation( EntityListeners.class ); EntityListeners entityListeners = currentClazz.getAnnotation( EntityListeners.class );
@ -282,4 +238,9 @@ public final class CallbackResolver {
} }
} }
} }
@Override
public void release() {
// nothing to do here
}
} }

View File

@ -21,31 +21,30 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.jpa;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import org.hibernate.internal.util.ReflectHelper;
/** /**
* Represents a JPA callback using a dedicated listener
*
* @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a> * @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
* @author Steve Ebersole
*/ */
public class ListenerCallback extends Callback { public class ListenerCallback implements Callback {
protected transient Object listener; private final Method callbackMethod;
private final Object listenerInstance;
public ListenerCallback(Method callbackMethod, Object listener) { public ListenerCallback(Object listenerInstance, Method callbackMethod) {
super( callbackMethod ); this.listenerInstance = listenerInstance;
this.listener = listener; this.callbackMethod = callbackMethod;
} }
@Override @Override
public void invoke(Object bean) { public void performCallback(Object entity) {
try { try {
callbackMethod.invoke( listener, new Object[]{bean} ); callbackMethod.invoke( listenerInstance, entity );
} }
catch (InvocationTargetException e) { catch (InvocationTargetException e) {
//keep runtime exceptions as is //keep runtime exceptions as is
@ -60,23 +59,4 @@ public class ListenerCallback extends Callback {
throw new RuntimeException( e ); throw new RuntimeException( e );
} }
} }
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject( listener.getClass().getName() );
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
String listenerClass = (String) ois.readObject();
try {
listener = ReflectHelper.classForName( listenerClass, this.getClass() ).newInstance();
}
catch (InstantiationException e) {
throw new ClassNotFoundException( "Unable to load class:" + listenerClass, e );
}
catch (IllegalAccessException e) {
throw new ClassNotFoundException( "Unable to load class:" + listenerClass, e );
}
}
} }

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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
*/
package org.hibernate.jpa.internal.event.jpa;
/**
* Factory for building instances user-specified event listeners.
*
* @author Steve Ebersole
*/
public interface ListenerFactory {
public <T> T buildListener(Class<T> listenerClass);
public void release();
}

View File

@ -0,0 +1,62 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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
*/
package org.hibernate.jpa.internal.event.jpa;
import javax.persistence.PersistenceException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 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 Map listenerInstances = new ConcurrentHashMap();
@Override
@SuppressWarnings("unchecked")
public <T> T buildListener(Class<T> 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
);
}
listenerInstances.put( listenerClass, listenerInstance );
}
return (T) listenerInstance;
}
@Override
public void release() {
listenerInstances.clear();
}
}

View File

@ -1,7 +1,7 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2009-2011, Red Hat Inc. or third-party contributors as * Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc. * distributed under license by Red Hat Inc.
@ -21,11 +21,8 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.jpa.internal.event; package org.hibernate.jpa.internal.event.jpa;
/** /**
* @author Emmanuel Bernard * Classes for integrating with JPA event callbacks
*/ */
public interface CallbackHandlerConsumer extends HibernateEntityManagerEventListener {
void setCallbackHandler(EntityCallbackHandler callbackHandler);
}

View File

@ -40,7 +40,7 @@ import org.jboss.logging.Logger;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.ejb.AvailableSettings; import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.internal.EntityManagerFactoryImpl; import org.hibernate.jpa.internal.EntityManagerFactoryImpl;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.jpa.HibernatePersistenceProvider; import org.hibernate.jpa.HibernatePersistenceProvider;

View File

@ -0,0 +1,66 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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
*/
package org.hibernate.jpa.test.cdi;
import javax.enterprise.inject.spi.BeanManager;
import java.util.Map;
import org.jboss.arquillian.container.weld.ee.embedded_1_1.mock.TestContainer;
import org.jboss.weld.bootstrap.api.Environments;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
/**
* @author Steve Ebersole
*/
public abstract class BaseCDIIntegrationTest extends BaseEntityManagerFunctionalTestCase {
private TestContainer testContainer;
@Override
@SuppressWarnings("unchecked")
protected void addConfigOptions(Map options) {
super.addConfigOptions( options );
testContainer = new TestContainer( getCdiBeans() );
testContainer.getBootstrap().startContainer( Environments.SE, testContainer.getDeployment() );
testContainer.getBootstrap().startInitialization();
testContainer.getBootstrap().deployBeans();
testContainer.getBootstrap().validateBeans().endInitialization();
options.put( AvailableSettings.CDI_BEAN_MANAGER, getBeanManager() );
}
protected BeanManager getBeanManager() {
return testContainer.getBeanManager( testContainer.getDeployment().getBeanDeploymentArchives().iterator().next() );
}
public abstract Class[] getCdiBeans();
@Override
public void releaseResources() {
super.releaseResources(); // closes the EMF
testContainer.stopContainer();
}
}

View File

@ -0,0 +1,166 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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
*/
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;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.PrePersist;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* @author Steve Ebersole
*/
public class BasicCDITest extends BaseCDIIntegrationTest {
private static int count;
@Override
public Class[] getCdiBeans() {
return new Class[] { EventQueue.class };
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { MyEntity.class };
}
@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 )
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 );
}
}
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() )
);
count++;
}
private String now() {
return new SimpleDateFormat().format( new Date() );
}
}
}