HHH-13890 : Add support for custom EventType and listeners - EventEngine

This commit is contained in:
Steve Ebersole 2020-04-17 18:32:19 -05:00
parent cecaeb92b3
commit 08bf2bb906
23 changed files with 756 additions and 1185 deletions

View File

@ -752,7 +752,6 @@ public interface SessionFactoryBuilder {
*/
SessionFactoryBuilder enableJpaClosedCompliance(boolean enabled);
/**
* Allows unwrapping this builder as another, more specific type.
*

View File

@ -373,8 +373,6 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
final ConfigurationService cfgService = sessionFactoryServiceRegistry.getService( ConfigurationService.class );
final ClassLoaderService classLoaderService = sessionFactoryServiceRegistry.getService( ClassLoaderService.class );
eventListenerRegistry.prepare( this );
for ( Map.Entry entry : ( (Map<?, ?>) cfgService.getSettings() ).entrySet() ) {
if ( !String.class.isInstance( entry.getKey() ) ) {
continue;

View File

@ -48,10 +48,13 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
private final SessionFactoryOptionsBuilder optionsBuilder;
public SessionFactoryBuilderImpl(MetadataImplementor metadata, BootstrapContext bootstrapContext) {
this( metadata, new SessionFactoryOptionsBuilder(
metadata.getMetadataBuildingOptions().getServiceRegistry(),
bootstrapContext
) );
this(
metadata,
new SessionFactoryOptionsBuilder(
metadata.getMetadataBuildingOptions().getServiceRegistry(),
bootstrapContext
)
);
}
public SessionFactoryBuilderImpl(MetadataImplementor metadata, SessionFactoryOptionsBuilder optionsBuilder) {
@ -271,7 +274,7 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
}
@Override
public SessionFactoryBuilder applyQuerySubstitutions(Map substitutions) {
public SessionFactoryBuilder applyQuerySubstitutions(@SuppressWarnings("rawtypes") Map substitutions) {
this.optionsBuilder.applyQuerySubstitutions( substitutions );
return this;
}
@ -472,5 +475,4 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
public SessionFactoryOptions buildSessionFactoryOptions() {
return optionsBuilder.buildOptions();
}
}

View File

@ -41,6 +41,7 @@ import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.hibernate.engine.profile.FetchProfile;
import org.hibernate.engine.query.spi.QueryPlanCache;
import org.hibernate.event.spi.EventEngine;
import org.hibernate.exception.spi.SQLExceptionConverter;
import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.id.IdentifierGenerator;
@ -140,6 +141,11 @@ public class SessionFactoryDelegatingImpl implements SessionFactoryImplementor,
return delegate.getStatistics();
}
@Override
public EventEngine getEventEngine() {
return delegate.getEventEngine();
}
@Override
public void close() throws HibernateException {
delegate.close();

View File

@ -32,6 +32,7 @@ import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.hibernate.engine.profile.FetchProfile;
import org.hibernate.engine.query.spi.QueryPlanCache;
import org.hibernate.event.spi.EventEngine;
import org.hibernate.exception.spi.SQLExceptionConverter;
import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.id.IdentifierGenerator;
@ -95,6 +96,11 @@ public interface SessionFactoryImplementor extends Mapping, SessionFactory, Quer
*/
ServiceRegistryImplementor getServiceRegistry();
/**
* Get the EventEngine associated with this SessionFactory
*/
EventEngine getEventEngine();
/**
* Get the factory scoped interceptor for this factory.
*

View File

@ -21,11 +21,14 @@ import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistrationException;
import org.hibernate.event.service.spi.JpaBootstrapSensitive;
import org.hibernate.event.spi.EventType;
import org.hibernate.jpa.event.spi.CallbackRegistry;
import org.hibernate.jpa.event.spi.CallbackRegistryConsumer;
import org.jboss.logging.Logger;
/**
* Standard EventListenerGroup implementation
*
* @author Steve Ebersole
* @author Sanne Grinovero
*/
@ -33,15 +36,20 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
private static final Logger log = Logger.getLogger( EventListenerGroupImpl.class );
private final EventType<T> eventType;
private final EventListenerRegistryImpl listenerRegistry;
private final CallbackRegistry callbackRegistry;
private final boolean isJpaBootstrap;
private final Set<DuplicationStrategy> duplicationStrategies = new LinkedHashSet<>();
private T[] listeners = null;
public EventListenerGroupImpl(EventType<T> eventType, EventListenerRegistryImpl listenerRegistry) {
public EventListenerGroupImpl(
EventType<T> eventType,
CallbackRegistry callbackRegistry,
boolean isJpaBootstrap) {
this.eventType = eventType;
this.listenerRegistry = listenerRegistry;
this.callbackRegistry = callbackRegistry;
this.isJpaBootstrap = isJpaBootstrap;
duplicationStrategies.add(
// At minimum make sure we do not register the same exact listener class multiple times.
@ -267,14 +275,12 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
}
private void performInjections(T listener) {
if ( CallbackRegistryConsumer.class.isInstance( listener ) ) {
( (CallbackRegistryConsumer) listener ).injectCallbackRegistry( listenerRegistry.getCallbackRegistry() );
if ( listener instanceof CallbackRegistryConsumer ) {
( (CallbackRegistryConsumer) listener ).injectCallbackRegistry( callbackRegistry );
}
if ( JpaBootstrapSensitive.class.isInstance( listener ) ) {
( (JpaBootstrapSensitive) listener ).wasJpaBootstrap(
listenerRegistry.getSessionFactory().getSessionFactoryOptions().isJpaBootstrap()
);
if ( listener instanceof JpaBootstrapSensitive ) {
( (JpaBootstrapSensitive) listener ).wasJpaBootstrap( isJpaBootstrap );
}
}

View File

@ -7,15 +7,14 @@
package org.hibernate.event.service.internal;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Function;
import org.hibernate.HibernateException;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.internal.DefaultAutoFlushEventListener;
import org.hibernate.event.internal.DefaultDeleteEventListener;
import org.hibernate.event.internal.DefaultDirtyCheckEventListener;
@ -40,16 +39,11 @@ import org.hibernate.event.internal.PostDeleteEventListenerStandardImpl;
import org.hibernate.event.internal.PostInsertEventListenerStandardImpl;
import org.hibernate.event.internal.PostUpdateEventListenerStandardImpl;
import org.hibernate.event.service.spi.DuplicationStrategy;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistrationException;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.jpa.event.internal.CallbackRegistryImplementor;
import org.hibernate.jpa.event.internal.CallbacksFactory;
import org.hibernate.jpa.event.spi.CallbackBuilder;
import org.hibernate.jpa.event.spi.CallbackRegistry;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.service.spi.Stoppable;
import static org.hibernate.event.spi.EventType.AUTO_FLUSH;
@ -90,126 +84,29 @@ import static org.hibernate.event.spi.EventType.SAVE_UPDATE;
import static org.hibernate.event.spi.EventType.UPDATE;
/**
* Standard implementation of EventListenerRegistry
*
* @author Steve Ebersole
*/
public class EventListenerRegistryImpl implements EventListenerRegistry, Stoppable {
private Map<Class,Object> listenerClassToInstanceMap = new HashMap<>();
@SuppressWarnings("rawtypes")
private final EventListenerGroup[] eventListeners;
private final Map<Class<?>,Object> listenerClassToInstanceMap = new HashMap<>();
private final SessionFactoryImplementor sessionFactory;
private final CallbackRegistryImplementor callbackRegistry;
private volatile EventListenerGroupImpl[] registeredEventListeners;
private CallbackBuilder callbackBuilder;
/**
* @deprecated Use {@link EventListenerRegistryImpl#EventListenerRegistryImpl(BootstrapContext, SessionFactoryImplementor)} instead
*/
@Deprecated
EventListenerRegistryImpl(
SessionFactoryImplementor sessionFactory,
SessionFactoryOptions sessionFactoryOptions,
ServiceRegistryImplementor registry) {
this.sessionFactory = sessionFactory;
this.callbackRegistry = CallbacksFactory.buildCallbackRegistry( sessionFactory );
this.registeredEventListeners = buildListenerGroups();
}
EventListenerRegistryImpl(BootstrapContext bootstrapContext, SessionFactoryImplementor sessionFactory) {
this.sessionFactory = sessionFactory;
this.callbackRegistry = CallbacksFactory.buildCallbackRegistry( sessionFactory );
this.callbackBuilder = CallbacksFactory.buildCallbackBuilder(
sessionFactory, bootstrapContext.getReflectionManager() );
this.registeredEventListeners = buildListenerGroups();
}
SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}
CallbackRegistry getCallbackRegistry() {
return callbackRegistry;
}
@Override
public void prepare(MetadataImplementor metadata) {
if ( callbackBuilder == null ) {
// TODO : not needed anymore when the deprecate constructor will be removed
this.callbackBuilder = CallbacksFactory.buildCallbackBuilder( sessionFactory, metadata.getMetadataBuildingOptions().getReflectionManager()
);
}
for ( PersistentClass persistentClass : metadata.getEntityBindings() ) {
if ( persistentClass.getClassName() == null ) {
// we can have non java class persisted by hibernate
continue;
}
callbackBuilder.buildCallbacksForEntity( persistentClass.getClassName(), callbackRegistry );
for ( Iterator propertyIterator = persistentClass.getDeclaredPropertyIterator();
propertyIterator.hasNext(); ) {
Property property = (Property) propertyIterator.next();
if ( property.getType().isComponentType() ) {
callbackBuilder.buildCallbacksForEmbeddable(
property,
persistentClass.getClassName(),
callbackRegistry
);
}
}
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private synchronized <T> EventListenerGroupImpl<T> getOrCreateEventListenerGroup(EventType<T> eventType) {
final int sizeOriginal = this.registeredEventListeners.length;
final EventListenerGroupImpl[] registeredEventListenersNew;
if ( eventType.ordinal() < sizeOriginal ) {
final EventListenerGroupImpl registeredEventListener = registeredEventListeners[ eventType.ordinal() ];
if ( registeredEventListener != null ) {
// eventType has already been registered;
return registeredEventListener; // EARLY RETURN
}
// eventType has not been registered yet.
// Its EventListenerGroupImpl will be created and added to registeredEventListeners below.
// There is already space for the new EventType in this.registeredEventListeners.
registeredEventListenersNew = this.registeredEventListeners;
}
else {
// eventType is a custom EventType, and there is not enough space in
// registeredEventListeners to accommodate it.
// Allocate a new array to hold listener groups for *all* EventType values that currently exist.
// This way an existing, unregistered EventType with a larger ordinal will not require another
// allocation when it gets registered in the future.
final int sizeNew = Math.max( eventType.ordinal() + 1, EventType.values().size() );
registeredEventListenersNew = new EventListenerGroupImpl[sizeNew];
// First copy the existing listeners to registeredEventListenersNew.
System.arraycopy( this.registeredEventListeners, 0, registeredEventListenersNew, 0, sizeOriginal );
}
final EventListenerGroupImpl listenerGroup = new EventListenerGroupImpl(
eventType,
EventListenerRegistryImpl.this
);
registeredEventListenersNew[eventType.ordinal()] = listenerGroup;
// Now update the reference.
this.registeredEventListeners = registeredEventListenersNew;
return listenerGroup;
@SuppressWarnings("rawtypes")
private EventListenerRegistryImpl(EventListenerGroup[] eventListeners) {
this.eventListeners = eventListeners;
}
@SuppressWarnings({ "unchecked" })
public <T> EventListenerGroupImpl<T> getEventListenerGroup(EventType<T> eventType) {
if ( registeredEventListeners.length < eventType.ordinal() + 1 ) {
public <T> EventListenerGroup<T> getEventListenerGroup(EventType<T> eventType) {
if ( eventListeners.length < eventType.ordinal() + 1 ) {
// eventTpe is a custom EventType that has not been registered.
// registeredEventListeners array was not allocated enough space to
// accommodate it.
throw new HibernateException( "Unable to find listeners for type [" + eventType.eventName() + "]" );
}
final EventListenerGroupImpl<T> listeners = registeredEventListeners[ eventType.ordinal() ];
final EventListenerGroup<T> listeners = eventListeners[ eventType.ordinal() ];
if ( listeners == null ) {
throw new HibernateException( "Unable to find listeners for type [" + eventType.eventName() + "]" );
}
@ -218,7 +115,8 @@ public class EventListenerRegistryImpl implements EventListenerRegistry, Stoppab
@Override
public void addDuplicationStrategy(DuplicationStrategy strategy) {
for ( EventListenerGroupImpl group : registeredEventListeners ) {
//noinspection rawtypes
for ( EventListenerGroup group : eventListeners ) {
if ( group != null ) {
group.addDuplicationStrategy( strategy );
}
@ -252,6 +150,7 @@ public class EventListenerRegistryImpl implements EventListenerRegistry, Stoppab
private <T> T instantiateListener(Class<T> listenerClass) {
try {
//noinspection deprecation
return listenerClass.newInstance();
}
catch ( Exception e ) {
@ -265,7 +164,7 @@ public class EventListenerRegistryImpl implements EventListenerRegistry, Stoppab
@Override
@SafeVarargs
public final <T> void setListeners(EventType<T> type, T... listeners) {
EventListenerGroupImpl<T> registeredListeners = getOrCreateEventListenerGroup( type );
final EventListenerGroup<T> registeredListeners = getEventListenerGroup( type );
registeredListeners.clear();
if ( listeners != null ) {
for ( T listener : listeners ) {
@ -283,7 +182,7 @@ public class EventListenerRegistryImpl implements EventListenerRegistry, Stoppab
@Override
@SafeVarargs
public final <T> void appendListeners(EventType<T> type, T... listeners) {
getOrCreateEventListenerGroup( type ).appendListeners( listeners );
getEventListenerGroup( type ).appendListeners( listeners );
}
@Override
@ -295,281 +194,206 @@ public class EventListenerRegistryImpl implements EventListenerRegistry, Stoppab
@Override
@SafeVarargs
public final <T> void prependListeners(EventType<T> type, T... listeners) {
getOrCreateEventListenerGroup( type ).prependListeners( listeners );
}
private EventListenerGroupImpl[] buildListenerGroups() {
EventListenerGroupImpl[] listenerArray = new EventListenerGroupImpl[ EventType.values().size() ];
// auto-flush listeners
prepareListeners(
AUTO_FLUSH,
new DefaultAutoFlushEventListener(),
listenerArray
);
// create listeners
prepareListeners(
PERSIST,
new DefaultPersistEventListener(),
listenerArray
);
// create-onflush listeners
prepareListeners(
PERSIST_ONFLUSH,
new DefaultPersistOnFlushEventListener(),
listenerArray
);
// delete listeners
prepareListeners(
DELETE,
new DefaultDeleteEventListener(),
listenerArray
);
// dirty-check listeners
prepareListeners(
DIRTY_CHECK,
new DefaultDirtyCheckEventListener(),
listenerArray
);
// evict listeners
prepareListeners(
EVICT,
new DefaultEvictEventListener(),
listenerArray
);
prepareListeners(
CLEAR,
listenerArray
);
// flush listeners
prepareListeners(
FLUSH,
new DefaultFlushEventListener(),
listenerArray
);
// flush-entity listeners
prepareListeners(
FLUSH_ENTITY,
new DefaultFlushEntityEventListener(),
listenerArray
);
// load listeners
prepareListeners(
LOAD,
new DefaultLoadEventListener(),
listenerArray
);
// resolve natural-id listeners
prepareListeners(
RESOLVE_NATURAL_ID,
new DefaultResolveNaturalIdEventListener(),
listenerArray
);
// load-collection listeners
prepareListeners(
INIT_COLLECTION,
new DefaultInitializeCollectionEventListener(),
listenerArray
);
// lock listeners
prepareListeners(
LOCK,
new DefaultLockEventListener(),
listenerArray
);
// merge listeners
prepareListeners(
MERGE,
new DefaultMergeEventListener(),
listenerArray
);
// pre-collection-recreate listeners
prepareListeners(
PRE_COLLECTION_RECREATE,
listenerArray
);
// pre-collection-remove listeners
prepareListeners(
PRE_COLLECTION_REMOVE,
listenerArray
);
// pre-collection-update listeners
prepareListeners(
PRE_COLLECTION_UPDATE,
listenerArray
);
// pre-delete listeners
prepareListeners(
PRE_DELETE,
listenerArray
);
// pre-insert listeners
prepareListeners(
PRE_INSERT,
listenerArray
);
// pre-load listeners
prepareListeners(
PRE_LOAD,
new DefaultPreLoadEventListener(),
listenerArray
);
// pre-update listeners
prepareListeners(
PRE_UPDATE,
listenerArray
);
// post-collection-recreate listeners
prepareListeners(
POST_COLLECTION_RECREATE,
listenerArray
);
// post-collection-remove listeners
prepareListeners(
POST_COLLECTION_REMOVE,
listenerArray
);
// post-collection-update listeners
prepareListeners(
POST_COLLECTION_UPDATE,
listenerArray
);
// post-commit-delete listeners
prepareListeners(
POST_COMMIT_DELETE,
listenerArray
);
// post-commit-insert listeners
prepareListeners(
POST_COMMIT_INSERT,
listenerArray
);
// post-commit-update listeners
prepareListeners(
POST_COMMIT_UPDATE,
listenerArray
);
// post-delete listeners
prepareListeners(
POST_DELETE,
new PostDeleteEventListenerStandardImpl(),
listenerArray
);
// post-insert listeners
prepareListeners(
POST_INSERT,
new PostInsertEventListenerStandardImpl(),
listenerArray
);
// post-load listeners
prepareListeners(
POST_LOAD,
new DefaultPostLoadEventListener(),
listenerArray
);
// post-update listeners
prepareListeners(
POST_UPDATE,
new PostUpdateEventListenerStandardImpl(),
listenerArray
);
// update listeners
prepareListeners(
UPDATE,
new DefaultUpdateEventListener(),
listenerArray
);
// refresh listeners
prepareListeners(
REFRESH,
new DefaultRefreshEventListener(),
listenerArray
);
// replicate listeners
prepareListeners(
REPLICATE,
new DefaultReplicateEventListener(),
listenerArray
);
// save listeners
prepareListeners(
SAVE,
new DefaultSaveEventListener(),
listenerArray
);
// save-update listeners
prepareListeners(
SAVE_UPDATE,
new DefaultSaveOrUpdateEventListener(),
listenerArray
);
return listenerArray;
}
private <T> void prepareListeners(EventType<T> type, EventListenerGroupImpl[] listenerArray) {
prepareListeners( type, null, listenerArray );
}
private <T> void prepareListeners(EventType<T> type, T defaultListener, EventListenerGroupImpl[] listenerArray) {
final EventListenerGroupImpl<T> listenerGroup;
if ( type == EventType.POST_COMMIT_DELETE
|| type == EventType.POST_COMMIT_INSERT
|| type == EventType.POST_COMMIT_UPDATE ) {
listenerGroup = new PostCommitEventListenerGroupImpl<T>( type, this );
}
else {
listenerGroup = new EventListenerGroupImpl<T>( type, this );
}
if ( defaultListener != null ) {
listenerGroup.appendListener( defaultListener );
}
listenerArray[ type.ordinal() ] = listenerGroup;
getEventListenerGroup( type ).prependListeners( listeners );
}
@Deprecated
@Override
public void stop() {
if ( callbackRegistry != null ) {
callbackRegistry.release();
// legacy - no longer used
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Builder
public static class Builder {
private final CallbackRegistryImplementor callbackRegistry;
private final boolean jpaBootstrap;
private final Map<EventType<?>,EventListenerGroup<?>> listenerGroupMap = new TreeMap<>(
Comparator.comparing( EventType::ordinal )
);
public Builder(CallbackRegistryImplementor callbackRegistry, boolean jpaBootstrap) {
this.callbackRegistry = callbackRegistry;
this.jpaBootstrap = jpaBootstrap;
applyStandardListeners();
}
if ( callbackBuilder != null ) {
callbackBuilder.release();
private void applyStandardListeners() {
// auto-flush listeners
prepareListeners( AUTO_FLUSH, new DefaultAutoFlushEventListener() );
// create listeners
prepareListeners( PERSIST, new DefaultPersistEventListener() );
// create-onflush listeners
prepareListeners( PERSIST_ONFLUSH, new DefaultPersistOnFlushEventListener() );
// delete listeners
prepareListeners( DELETE, new DefaultDeleteEventListener() );
// dirty-check listeners
prepareListeners( DIRTY_CHECK, new DefaultDirtyCheckEventListener() );
// evict listeners
prepareListeners( EVICT, new DefaultEvictEventListener() );
prepareListeners( CLEAR );
// flush listeners
prepareListeners( FLUSH, new DefaultFlushEventListener() );
// flush-entity listeners
prepareListeners( FLUSH_ENTITY, new DefaultFlushEntityEventListener() );
// load listeners
prepareListeners( LOAD, new DefaultLoadEventListener() );
// resolve natural-id listeners
prepareListeners( RESOLVE_NATURAL_ID, new DefaultResolveNaturalIdEventListener() );
// load-collection listeners
prepareListeners( INIT_COLLECTION, new DefaultInitializeCollectionEventListener() );
// lock listeners
prepareListeners( LOCK, new DefaultLockEventListener() );
// merge listeners
prepareListeners( MERGE, new DefaultMergeEventListener() );
// pre-collection-recreate listeners
prepareListeners( PRE_COLLECTION_RECREATE );
// pre-collection-remove listeners
prepareListeners( PRE_COLLECTION_REMOVE );
// pre-collection-update listeners
prepareListeners( PRE_COLLECTION_UPDATE );
// pre-delete listeners
prepareListeners( PRE_DELETE );
// pre-insert listeners
prepareListeners( PRE_INSERT );
// pre-load listeners
prepareListeners( PRE_LOAD, new DefaultPreLoadEventListener() );
// pre-update listeners
prepareListeners( PRE_UPDATE );
// post-collection-recreate listeners
prepareListeners( POST_COLLECTION_RECREATE );
// post-collection-remove listeners
prepareListeners( POST_COLLECTION_REMOVE );
// post-collection-update listeners
prepareListeners( POST_COLLECTION_UPDATE );
// post-commit-delete listeners
prepareListeners( POST_COMMIT_DELETE );
// post-commit-insert listeners
prepareListeners( POST_COMMIT_INSERT );
// post-commit-update listeners
prepareListeners( POST_COMMIT_UPDATE );
// post-delete listeners
prepareListeners( POST_DELETE, new PostDeleteEventListenerStandardImpl() );
// post-insert listeners
prepareListeners( POST_INSERT, new PostInsertEventListenerStandardImpl() );
// post-load listeners
prepareListeners( POST_LOAD, new DefaultPostLoadEventListener() );
// post-update listeners
prepareListeners( POST_UPDATE, new PostUpdateEventListenerStandardImpl() );
// update listeners
prepareListeners( UPDATE, new DefaultUpdateEventListener() );
// refresh listeners
prepareListeners( REFRESH, new DefaultRefreshEventListener() );
// replicate listeners
prepareListeners( REPLICATE, new DefaultReplicateEventListener() );
// save listeners
prepareListeners( SAVE, new DefaultSaveEventListener() );
// save-update listeners
prepareListeners( SAVE_UPDATE, new DefaultSaveOrUpdateEventListener() );
}
public <T> void prepareListeners(EventType<T> eventType) {
prepareListeners( eventType, null );
}
public <T> void prepareListeners(EventType<T> type, T defaultListener) {
prepareListeners(
type,
defaultListener,
t -> {
if ( type == EventType.POST_COMMIT_DELETE
|| type == EventType.POST_COMMIT_INSERT
|| type == EventType.POST_COMMIT_UPDATE ) {
return new PostCommitEventListenerGroupImpl<>( type, callbackRegistry, jpaBootstrap );
}
else {
return new EventListenerGroupImpl<>( type, callbackRegistry, jpaBootstrap );
}
}
);
}
@SuppressWarnings({"rawtypes", "unchecked"})
public <T> void prepareListeners(
EventType<T> type,
T defaultListener,
Function<EventType<T>,EventListenerGroupImpl<T>> groupCreator) {
final EventListenerGroupImpl listenerGroup = groupCreator.apply( type );
if ( defaultListener != null ) {
listenerGroup.appendListener( defaultListener );
}
listenerGroupMap.put( type, listenerGroup );
}
public <T> EventListenerGroup<T> getListenerGroup(EventType<T> eventType) {
//noinspection unchecked
return (EventListenerGroup<T>) listenerGroupMap.get( eventType );
}
@SuppressWarnings("rawtypes")
public EventListenerRegistry buildRegistry(Map<String, EventType> registeredEventTypes) {
// validate contiguity of the event-type ordinals and build the EventListenerGroups array
final ArrayList<EventType> eventTypeList = new ArrayList<>( registeredEventTypes.values() );
eventTypeList.sort( Comparator.comparing( EventType::ordinal ) );
final EventListenerGroup[] eventListeners = new EventListenerGroup[ eventTypeList.size() ];
int previous = -1;
for ( int i = 0; i < eventTypeList.size(); i++ ) {
final EventType eventType = eventTypeList.get( i );
assert i == eventType.ordinal();
assert i - 1 == previous;
eventListeners[i] = listenerGroupMap.get( eventType );
previous = i;
}
return new EventListenerRegistryImpl( eventListeners );
}
}
}

View File

@ -1,41 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.event.service.internal;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.service.spi.SessionFactoryServiceInitiator;
import org.hibernate.service.spi.SessionFactoryServiceInitiatorContext;
/**
* Service initiator for {@link EventListenerRegistry}
*
* @author Steve Ebersole
*/
public class EventListenerServiceInitiator implements SessionFactoryServiceInitiator<EventListenerRegistry> {
public static final EventListenerServiceInitiator INSTANCE = new EventListenerServiceInitiator();
@Override
public Class<EventListenerRegistry> getServiceInitiated() {
return EventListenerRegistry.class;
}
@Override
public EventListenerRegistry initiateService(
SessionFactoryImplementor sessionFactory,
SessionFactoryOptions sessionFactoryOptions,
ServiceRegistryImplementor registry) {
return new EventListenerRegistryImpl( sessionFactory, sessionFactoryOptions, registry );
}
@Override
public EventListenerRegistry initiateService(SessionFactoryServiceInitiatorContext context) {
return new EventListenerRegistryImpl( context.getSessionFactory(), context.getSessionFactoryOptions(), context.getServiceRegistry());
}
}

View File

@ -12,6 +12,7 @@ import org.hibernate.event.spi.PostCommitInsertEventListener;
import org.hibernate.event.spi.PostCommitUpdateEventListener;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.jpa.event.spi.CallbackRegistry;
/**
* Historically, the listeners for the post-commit events simply reused the
@ -27,8 +28,11 @@ class PostCommitEventListenerGroupImpl<T> extends EventListenerGroupImpl<T> {
private final Class extendedListenerContract;
public PostCommitEventListenerGroupImpl(EventType<T> eventType, EventListenerRegistryImpl listenerRegistry) {
super( eventType, listenerRegistry );
public PostCommitEventListenerGroupImpl(
EventType<T> eventType,
CallbackRegistry callbackRegistry,
boolean isJpaBootstrap) {
super( eventType, callbackRegistry, isJpaBootstrap );
if ( eventType == EventType.POST_COMMIT_DELETE ) {
this.extendedListenerContract = PostCommitDeleteEventListener.class;

View File

@ -19,7 +19,14 @@ import org.hibernate.service.Service;
* @author Steve Ebersole
*/
public interface EventListenerRegistry extends Service, Serializable {
void prepare(MetadataImplementor metadata);
/**
* @deprecated this method was only ever used to initialize the CallbackRegistry
* which is now managed as part of the EventEngine
*/
@Deprecated
default void prepare(MetadataImplementor metadata) {
// by default do nothing now
}
<T> EventListenerGroup<T> getEventListenerGroup(EventType<T> eventType);

View File

@ -0,0 +1,198 @@
/*
* 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.event.spi;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.function.Consumer;
import org.hibernate.HibernateException;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.service.internal.EventListenerRegistryImpl;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.jpa.event.internal.CallbackRegistryImplementor;
import org.hibernate.jpa.event.internal.CallbacksFactory;
import org.hibernate.jpa.event.spi.CallbackBuilder;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.service.spi.Stoppable;
/**
* Composite for the things related to Hibernate's event system.
*
* @author Steve Ebersole
*/
public class EventEngine {
@SuppressWarnings("rawtypes")
private final Map<String,EventType> registeredEventTypes;
private final EventListenerRegistry listenerRegistry;
private final CallbackRegistryImplementor callbackRegistry;
private final CallbackBuilder callbackBuilder;
public EventEngine(
MetadataImplementor mappings,
SessionFactoryImplementor sessionFactory) {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// resolve (JPA) callback handlers
this.callbackRegistry = CallbacksFactory.buildCallbackRegistry( sessionFactory.getSessionFactoryOptions() );
this.callbackBuilder = CallbacksFactory.buildCallbackBuilder(
sessionFactory.getSessionFactoryOptions(),
sessionFactory.getServiceRegistry(),
mappings.getMetadataBuildingOptions().getReflectionManager()
);
for ( PersistentClass persistentClass : mappings.getEntityBindings() ) {
if ( persistentClass.getClassName() == null ) {
// we can have dynamic (non-java class) mapping
continue;
}
this.callbackBuilder.buildCallbacksForEntity( persistentClass.getClassName(), callbackRegistry );
for ( Iterator<Property> propertyIterator = persistentClass.getDeclaredPropertyIterator(); propertyIterator.hasNext(); ) {
final Property property = propertyIterator.next();
if ( property.getType().isComponentType() ) {
this.callbackBuilder.buildCallbacksForEmbeddable(
property,
persistentClass.getClassName(),
callbackRegistry
);
}
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// resolve event types and listeners
final EventListenerRegistryImpl.Builder listenerRegistryBuilder = new EventListenerRegistryImpl.Builder(
callbackRegistry,
sessionFactory.getSessionFactoryOptions().isJpaBootstrap()
);
final Map<String,EventType> eventTypes = new HashMap<>();
EventType.registerStandardTypes( eventTypes );
final EventEngineContributions contributionManager = new EventEngineContributions() {
@Override
public <T> EventType<T> findEventType(String name) {
//noinspection unchecked
return eventTypes.get( name );
}
@Override
public <T> EventType<T> contributeEventType(String name, Class<T> listenerRole) {
final EventType<T> eventType = registerEventType( name, listenerRole );
listenerRegistryBuilder.prepareListeners( eventType );
return eventType;
}
private <T> EventType<T> registerEventType(String name, Class<T> listenerRole) {
if ( name == null ) {
throw new HibernateException( "Custom event-type name must be non-null." );
}
if ( listenerRole == null ) {
throw new HibernateException( "Custom event-type listener role must be non-null." );
}
// make sure it does not match an existing name...
if ( eventTypes.containsKey( name ) ) {
final EventType<?> existing = eventTypes.get( name );
throw new HibernateException(
"Custom event-type already registered: " + name + " => " + existing
);
}
final EventType<T> eventType = EventType.create(
name,
listenerRole,
eventTypes.size()
);
eventTypes.put( name, eventType );
return eventType;
}
@Override
public <T> EventType<T> contributeEventType(String name, Class<T> listenerRole, T... defaultListeners) {
final EventType<T> eventType = contributeEventType( name, listenerRole );
if ( defaultListeners != null ) {
final EventListenerGroup<T> listenerGroup = listenerRegistryBuilder.getListenerGroup( eventType );
listenerGroup.appendListeners( defaultListeners );
}
return eventType;
}
@Override
public <T> void configureListeners(
EventType<T> eventType,
Consumer<EventListenerGroup<T>> action) {
if ( ! eventTypes.containsValue( eventType ) ) {
throw new HibernateException( "EventType [" + eventType + "] not registered" );
}
action.accept( listenerRegistryBuilder.getListenerGroup( eventType ) );
}
};
final Collection<EventEngineContributor> discoveredContributors = sessionFactory.getServiceRegistry()
.getService( ClassLoaderService.class )
.loadJavaServices( EventEngineContributor.class );
if ( CollectionHelper.isNotEmpty( discoveredContributors ) ) {
for ( EventEngineContributor contributor : discoveredContributors ) {
contributor.contribute( contributionManager );
}
}
this.registeredEventTypes = Collections.unmodifiableMap( eventTypes );
this.listenerRegistry = listenerRegistryBuilder.buildRegistry( registeredEventTypes );
}
public Collection<EventType<?>> getRegisteredEventTypes() {
//noinspection unchecked,rawtypes
return (Collection) registeredEventTypes.values();
}
public <T> EventType<T> findRegisteredEventType(String name) {
//noinspection unchecked
return registeredEventTypes.get( name );
}
public EventListenerRegistry getListenerRegistry() {
return listenerRegistry;
}
public CallbackRegistryImplementor getCallbackRegistry() {
return callbackRegistry;
}
public void stop() {
if ( listenerRegistry instanceof Stoppable ) {
( (Stoppable) listenerRegistry ).stop();
}
callbackRegistry.release();
callbackBuilder.release();
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.event.spi;
import java.util.function.Consumer;
import org.hibernate.event.service.spi.EventListenerGroup;
/**
* Callback for {@link EventEngineContributor}
*
* @author Steve Ebersole
*/
public interface EventEngineContributions {
/**
* Return the EventType by name, if one
*/
<T> EventType<T> findEventType(String name);
/**
* Register a custom event type.
*
* @apiNote We except the "raw" state rather than an `EventType` instance to account for
* the `EventType#ordinal` property. All registered types must be contiguous, so we handle
* the ordinality behind the scenes
*/
<T> EventType<T> contributeEventType(String name, Class<T> listenerRole);
/**
* Register a custom event type with a default listener.
*/
<T> EventType<T> contributeEventType(String name, Class<T> listenerRole, T... defaultListener);
/**
* Perform an action against the listener group for the specified event-type
*/
<T> void configureListeners(EventType<T> eventType, Consumer<EventListenerGroup<T>> action);
}

View File

@ -0,0 +1,21 @@
/*
* 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.event.spi;
/**
* Integration contract for contributing event types and listeners to the Hibernate event system.
*
* Discoverable via Java's service loading mechanism ({@link java.util.ServiceLoader})
*
* @author Steve Ebersole
*/
public interface EventEngineContributor {
/**
* Apply the contributions
*/
void contribute(EventEngineContributions target);
}

View File

@ -10,14 +10,12 @@ import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.hibernate.HibernateException;
import org.hibernate.internal.CoreLogging;
import org.jboss.logging.Logger;
/**
* Enumeration of the recognized types of events, including meta-information about each.
@ -25,8 +23,10 @@ import org.jboss.logging.Logger;
* @author Steve Ebersole
*/
public final class EventType<T> {
private static final Logger LOG = CoreLogging.logger( EventType.class );
private static AtomicInteger typeCounter = new AtomicInteger( 0 );
/**
* Used to assign ordinals for the standard event-types
*/
private static AtomicInteger STANDARD_TYPE_COUNTER = new AtomicInteger( 0 );
public static final EventType<LoadEventListener> LOAD = create( "load", LoadEventListener.class );
public static final EventType<ResolveNaturalIdEventListener> RESOLVE_NATURAL_ID = create( "resolve-natural-id", ResolveNaturalIdEventListener.class );
@ -79,60 +79,16 @@ public final class EventType<T> {
public static final EventType<PostCollectionRemoveEventListener> POST_COLLECTION_REMOVE = create( "post-collection-remove", PostCollectionRemoveEventListener.class );
public static final EventType<PostCollectionUpdateEventListener> POST_COLLECTION_UPDATE = create( "post-collection-update", PostCollectionUpdateEventListener.class );
/**
* Add a new event type.
*
* @param name - name of the custom event
* @param listenerClass - the base listener class or interface associated with the entity type
* @param <T> - listenerClass
* @return the custom {@link EventType}
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static synchronized <T> EventType<T> addCustomEventType(String name, Class<T> listenerClass) {
if ( name == null || listenerClass == null ) {
throw new HibernateException( "Custom EventType name and associated class must be non-null." );
}
final EventType eventType = EVENT_TYPE_BY_NAME_MAP.computeIfAbsent(
name,
( e -> {
final EventType eventTypeNew = EventType.create( name, listenerClass );
LOG.debugf(
"Added custom EventType: [%s], ordinal=[%d], listener=[%s].",
name,
eventTypeNew.ordinal,
listenerClass.toString()
);
return eventTypeNew;
} )
);
// There's no way to know if there was a pre-existing EventType with
// the same name and listener, so ignore that case.
// Just check that listener is the same as listenerClass
if ( !listenerClass.equals( eventType.baseListenerInterface ) ) {
throw new HibernateException(
"Could not add EventType [" + name + "] with listener Class ["
+ "]. An EventType with that name already exists with listener ["
+ listenerClass.getName()
+ "]."
);
}
return eventType;
}
private static <T> EventType<T> create(String name, Class<T> listenerClass) {
return new EventType<T>( name, listenerClass );
}
/**
* Maintain a map of {@link EventType} instances keyed by name for lookup by name as well as {@link #values()}
* resolution.
*/
private static final Map<String,EventType> EVENT_TYPE_BY_NAME_MAP = AccessController.doPrivileged(
@SuppressWarnings({"rawtypes", "Convert2Lambda"})
private static final Map<String,EventType> STANDARD_TYPE_BY_NAME_MAP = AccessController.doPrivileged(
new PrivilegedAction<Map<String, EventType>>() {
@Override
public Map<String, EventType> run() {
final Map<String, EventType> typeByNameMap = new ConcurrentHashMap<>();
final Map<String, EventType> typeByNameMap = new HashMap<>();
for ( Field field : EventType.class.getDeclaredFields() ) {
if ( EventType.class.isAssignableFrom( field.getType() ) ) {
try {
@ -144,11 +100,20 @@ public final class EventType<T> {
}
}
}
return typeByNameMap;
return Collections.unmodifiableMap( typeByNameMap );
}
}
);
private static <T> EventType<T> create(String name, Class<T> listenerRole) {
return new EventType<>( name, listenerRole, STANDARD_TYPE_COUNTER.getAndIncrement(), true );
}
public static <T> EventType<T> create(String name, Class<T> listenerRole, int ordinal) {
return new EventType<>( name, listenerRole, ordinal, false );
}
/**
* Find an {@link EventType} by its name
*
@ -158,11 +123,12 @@ public final class EventType<T> {
*
* @throws HibernateException If eventName is null, or if eventName does not correlate to any known event type.
*/
@SuppressWarnings("rawtypes")
public static EventType resolveEventTypeByName(final String eventName) {
if ( eventName == null ) {
throw new HibernateException( "event name to resolve cannot be null" );
}
final EventType eventType = EVENT_TYPE_BY_NAME_MAP.get( eventName );
final EventType eventType = STANDARD_TYPE_BY_NAME_MAP.get( eventName );
if ( eventType == null ) {
throw new HibernateException( "Unable to locate proper event type for event name [" + eventName + "]" );
}
@ -170,37 +136,44 @@ public final class EventType<T> {
}
/**
* Get a collection of all {@link EventType} instances.
*
* @return All {@link EventType} instances
* Get a collection of all the standard {@link EventType} instances.
*/
@SuppressWarnings("rawtypes")
public static Collection<EventType> values() {
return EVENT_TYPE_BY_NAME_MAP.values();
return STANDARD_TYPE_BY_NAME_MAP.values();
}
/**
* Used from {@link EventEngine} to "prime" the registered event-type map.
*
* Simply copy the values into its (passed) Map
*/
@SuppressWarnings("rawtypes")
static void registerStandardTypes(Map<String, EventType> eventTypes) {
eventTypes.putAll( STANDARD_TYPE_BY_NAME_MAP );
}
private final String eventName;
private final Class<T> baseListenerInterface;
private final int ordinal;
private final boolean isStandardEvent;
private EventType(String eventName, Class<T> baseListenerInterface) {
private EventType(String eventName, Class<T> baseListenerInterface, int ordinal, boolean isStandardEvent) {
this.eventName = eventName;
this.baseListenerInterface = baseListenerInterface;
this.ordinal = typeCounter.getAndIncrement();
this.ordinal = ordinal;
this.isStandardEvent = isStandardEvent;
}
public String eventName() {
return eventName;
}
@SuppressWarnings("rawtypes")
public Class baseListenerInterface() {
return baseListenerInterface;
}
@Override
public String toString() {
return eventName();
}
/**
* EventType is effectively an enumeration. Since there is a known, limited number of possible types, we expose an
* ordinal for each in order to be able to efficiently do associations elsewhere in the codebase (array vs. Map)
@ -213,4 +186,15 @@ public final class EventType<T> {
return ordinal;
}
/**
* Is this event-type one of the standard event-types?
*/
public boolean isStandardEvent() {
return isStandardEvent;
}
@Override
public String toString() {
return eventName();
}
}

View File

@ -84,6 +84,7 @@ import org.hibernate.engine.spi.SessionOwner;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventEngine;
import org.hibernate.event.spi.EventType;
import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.id.IdentifierGenerator;
@ -167,6 +168,7 @@ public class SessionFactoryImpl implements SessionFactoryImplementor {
private final transient Map<String,Object> properties;
private final transient SessionFactoryServiceRegistry serviceRegistry;
private final transient EventEngine eventEngine;
private final transient JdbcServices jdbcServices;
private final transient SQLFunctionRegistry sqlFunctionRegistry;
@ -208,6 +210,8 @@ public class SessionFactoryImpl implements SessionFactoryImplementor {
.getService( SessionFactoryServiceRegistryFactory.class )
.buildServiceRegistry( this, options );
this.eventEngine = new EventEngine( metadata, this );
metadata.initSessionFactory( this );
final CfgXmlAccessService cfgXmlAccessService = serviceRegistry.getService( CfgXmlAccessService.class );
@ -518,6 +522,11 @@ public class SessionFactoryImpl implements SessionFactoryImplementor {
return name;
}
@Override
public EventEngine getEventEngine() {
return eventEngine;
}
@Override
public JdbcServices getJdbcServices() {
return jdbcServices;

View File

@ -11,6 +11,7 @@ import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.jpa.event.spi.CallbackBuilder;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.service.ServiceRegistry;
/**
* The intent of this class is to use a lighter implementation
@ -18,9 +19,8 @@ import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
* {@link org.hibernate.boot.spi.SessionFactoryOptions#areJPACallbacksEnabled()}
*/
public final class CallbacksFactory {
public static CallbackRegistryImplementor buildCallbackRegistry(SessionFactoryImplementor sessionFactory) {
if ( jpaCallBacksEnabled( sessionFactory ) ) {
public static CallbackRegistryImplementor buildCallbackRegistry(SessionFactoryOptions options) {
if ( jpaCallBacksEnabled( options ) ) {
return new CallbackRegistryImpl();
}
else {
@ -29,10 +29,11 @@ public final class CallbacksFactory {
}
public static CallbackBuilder buildCallbackBuilder(
SessionFactoryImplementor sessionFactory,
SessionFactoryOptions options,
ServiceRegistry serviceRegistry,
ReflectionManager reflectionManager) {
if ( jpaCallBacksEnabled( sessionFactory ) ) {
final ManagedBeanRegistry managedBeanRegistry = sessionFactory.getServiceRegistry().getService( ManagedBeanRegistry.class );
if ( jpaCallBacksEnabled( options ) ) {
final ManagedBeanRegistry managedBeanRegistry = serviceRegistry.getService( ManagedBeanRegistry.class );
return new CallbackBuilderLegacyImpl(
managedBeanRegistry,
reflectionManager
@ -43,8 +44,7 @@ public final class CallbacksFactory {
}
}
private static boolean jpaCallBacksEnabled(SessionFactoryImplementor sessionFactory) {
SessionFactoryOptions options = sessionFactory.getSessionFactoryOptions();
private static boolean jpaCallBacksEnabled(SessionFactoryOptions options) {
return options.areJPACallbacksEnabled();
}

View File

@ -7,7 +7,6 @@
package org.hibernate.service.internal;
import java.util.List;
import java.util.ListIterator;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.engine.config.spi.ConfigurationService;
@ -22,6 +21,8 @@ import org.hibernate.service.spi.SessionFactoryServiceInitiator;
import org.hibernate.service.spi.SessionFactoryServiceInitiatorContext;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/
@ -29,11 +30,12 @@ public class SessionFactoryServiceRegistryImpl
extends AbstractServiceRegistryImpl
implements SessionFactoryServiceRegistry, SessionFactoryServiceInitiatorContext {
private static final Logger log = Logger.getLogger( SessionFactoryServiceRegistryImpl.class );
private final SessionFactoryOptions sessionFactoryOptions;
private final SessionFactoryImplementor sessionFactory;
private EventListenerRegistry cachedEventListenerRegistry;
@SuppressWarnings( {"unchecked"})
@SuppressWarnings({"unchecked", "rawtypes"})
public SessionFactoryServiceRegistryImpl(
ServiceRegistryImplementor parent,
List<SessionFactoryServiceInitiator> initiators,
@ -64,7 +66,7 @@ public class SessionFactoryServiceRegistryImpl
@Override
public <R extends Service> void configureService(ServiceBinding<R> serviceBinding) {
if ( Configurable.class.isInstance( serviceBinding.getService() ) ) {
if ( serviceBinding.getService() instanceof Configurable ) {
( (Configurable) serviceBinding.getService() ).configure( getService( ConfigurationService.class ).getSettings() );
}
}
@ -86,22 +88,16 @@ public class SessionFactoryServiceRegistryImpl
@Override
public <R extends Service> R getService(Class<R> serviceRole) {
//HHH-11051 cache EventListenerRegistry
if ( serviceRole.equals( EventListenerRegistry.class ) ) {
if ( cachedEventListenerRegistry == null ) {
cachedEventListenerRegistry = (EventListenerRegistry) super.getService( serviceRole );
}
return (R) cachedEventListenerRegistry;
log.debug(
"EventListenerRegistry access via ServiceRegistry is deprecated. " +
"Use `sessionFactory.getEventEngine().getListenerRegistry()` instead"
);
//noinspection unchecked
return (R) sessionFactory.getEventEngine().getListenerRegistry();
}
return super.getService( serviceRole );
}
@Override
public synchronized void destroy() {
super.destroy();
this.cachedEventListenerRegistry = null;
}
}

View File

@ -7,12 +7,10 @@
package org.hibernate.service.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.hibernate.engine.query.spi.NativeQueryInterpreterInitiator;
import org.hibernate.engine.spi.CacheInitiator;
import org.hibernate.event.service.internal.EventListenerServiceInitiator;
import org.hibernate.service.spi.SessionFactoryServiceInitiator;
import org.hibernate.stat.internal.StatisticsInitiator;
@ -27,7 +25,6 @@ public final class StandardSessionFactoryServiceInitiators {
public static List<SessionFactoryServiceInitiator> buildStandardServiceInitiatorList() {
final ArrayList<SessionFactoryServiceInitiator> serviceInitiators = new ArrayList<>();
serviceInitiators.add( EventListenerServiceInitiator.INSTANCE );
serviceInitiators.add( StatisticsInitiator.INSTANCE );
serviceInitiators.add( CacheInitiator.INSTANCE );
serviceInitiators.add( NativeQueryInterpreterInitiator.INSTANCE );

View File

@ -1,71 +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.event;
import org.hibernate.HibernateException;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.LoadEventListener;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Gail Badner
*/
@TestForIssue( jiraKey = "HHH-13890" )
public class CustomEventTypeTest {
private final String EVENT_TYPE_NAME = "operation";
private final String OTHER_EVENT_TYPE_NAME = "other-operation";
@Test
public void testAddCustomEventType() {
final int numberOfEventTypesOriginal = EventType.values().size();
try {
EventType.resolveEventTypeByName( EVENT_TYPE_NAME );
fail( "Should have thrown HibernateException" );
}
catch(HibernateException expected) {
}
final EventType<CustomListener> eventType = EventType.addCustomEventType( EVENT_TYPE_NAME, CustomListener.class );
assertEquals( EVENT_TYPE_NAME, eventType.eventName() );
assertEquals( CustomListener.class, eventType.baseListenerInterface() );
assertEquals( numberOfEventTypesOriginal, eventType.ordinal() );
assertTrue( EventType.values().contains( eventType ) );
assertEquals( numberOfEventTypesOriginal + 1, EventType.values().size() );
final EventType<OtherCustomListener> otherEventType = EventType.addCustomEventType( OTHER_EVENT_TYPE_NAME, OtherCustomListener.class );
assertEquals( OTHER_EVENT_TYPE_NAME, otherEventType.eventName() );
assertEquals( OtherCustomListener.class, otherEventType.baseListenerInterface() );
assertEquals( numberOfEventTypesOriginal + 1, otherEventType.ordinal() );
assertEquals( numberOfEventTypesOriginal + 2, EventType.values().size() );
// Adding an event type with the same name and base listener as one that exists, should be OK.
EventType.addCustomEventType( "load", LoadEventListener.class );
// Adding an event type with the same name but different listener as one that exists, should fail.
try {
EventType.addCustomEventType( "load", CustomListener.class );
fail( "Should have thrown HibernateException" );
}
catch (HibernateException expected) {
}
}
public interface CustomListener {
}
public interface OtherCustomListener {
}
}

View File

@ -2,7 +2,6 @@ package org.hibernate.event.service.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.hibernate.event.service.spi.DuplicationStrategy;
import org.hibernate.event.service.spi.EventListenerGroup;
@ -32,7 +31,7 @@ public class EventListenerDuplicationStrategyTest {
Tracker tracker = new Tracker();
ClearEvent event = new ClearEvent( null );
EventListenerGroup<ClearEventListener> listenerGroup = new EventListenerGroupImpl( EventType.CLEAR, null );
EventListenerGroup<ClearEventListener> listenerGroup = new EventListenerGroupImpl( EventType.CLEAR, null, false );
@Test
public void testListenersIterator() {

View File

@ -1,396 +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.test.events;
import java.util.concurrent.atomic.AtomicInteger;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.HibernateException;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.jpa.event.spi.CallbackRegistry;
import org.hibernate.jpa.event.spi.CallbackRegistryConsumer;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
@TestForIssue( jiraKey = "HHH-13890")
public class CustomEventTypeRegisterListenerTest extends BaseCoreFunctionalTestCase {
public enum Category {
CLOTHING,
FURNITURE
}
private final TheIntegrator theIntegrator = new TheIntegrator();
@Override
protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) {
builder.applyIntegrator( theIntegrator );
}
@Test
public void testSetListenerClasses() {
testNormalUsage( theIntegrator.eventTypeForSetListenerClasses(), UsesSetClasses.class );
}
@Test
public void testSetListenerObjects() {
testNormalUsage( theIntegrator.eventTypeForSetListenerObjects(), UsesSetObjects.class );
}
@Test
public void testAppendListenerClasses() {
testNormalUsage( theIntegrator.eventTypeForAppendListenerClasses(), UsesAppendClasses.class );
}
@Test
public void testAppendListenerObjects() {
testNormalUsage( theIntegrator.eventTypeForAppendListenerObjects(), UsesAppendObjects.class );
}
@Test
public void testPrependListenerClasses() {
testNormalUsage( theIntegrator.eventTypeForPrependListenerClasses(), UsesPrependClasses.class );
}
@Test
public void testPrependListenerObjects() {
testNormalUsage( theIntegrator.eventTypeForPrependListenerObjects(), UsesPrependObjects.class );
}
@Test
public void testUnregisteredEventType() {
final EventListenerRegistry eventListenerRegistry =
sessionFactory().getServiceRegistry().getService( EventListenerRegistry.class );
try {
eventListenerRegistry.getEventListenerGroup( theIntegrator.eventTypeUnregistered() );
fail( "HibernateException should have been thrown." );
}
catch (HibernateException expected) {
}
}
private <T extends Listener> void testNormalUsage(EventType<T> eventType, Class<T> baseListenerClass) {
final Item clothing = new Item( Category.CLOTHING );
final Item furniture = new Item( Category.FURNITURE );
final Item other = new Item();
final EventListenerRegistry eventListenerRegistry =
sessionFactory().getServiceRegistry().getService( EventListenerRegistry.class );
final EventListenerGroup<T> group =
eventListenerRegistry.getEventListenerGroup( eventType );
for ( Object listener : group.listeners() ) {
assertNotNull( ( (ItemNameGeneratorListener) listener).getCallbackRegistry() );
}
final ItemNameGeneratorEvent clothingEvent = new ItemNameGeneratorEvent( clothing );
group.fireEventOnEachListener( clothingEvent, Listener::onGenerateItemName );
assertEquals( "C100", clothing.name );
final ItemNameGeneratorEvent furnitureEvent = new ItemNameGeneratorEvent( furniture );
group.fireEventOnEachListener( furnitureEvent, Listener::onGenerateItemName );
assertEquals( "F200", furniture.name );
final ItemNameGeneratorEvent otherEvent = new ItemNameGeneratorEvent( other );
group.fireEventOnEachListener( otherEvent, Listener::onGenerateItemName );
assertEquals( "O300", other.name );
}
@Entity(name = "Item")
public static class Item {
@Id
private int id;
private Category category;
private String name;
Item() {
}
Item(Category category) {
this.category = category;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static class ItemNameGeneratorEvent {
private Item item;
public ItemNameGeneratorEvent(Item item) {
this.item = item;
}
public Item getItem() {
return item;
}
}
public interface Listener {
void onGenerateItemName(ItemNameGeneratorEvent event);
}
public interface ItemNameGeneratorListener extends Listener, CallbackRegistryConsumer {
CallbackRegistry getCallbackRegistry();
}
public interface UsesSetClasses extends Listener {
}
public interface UsesSetObjects extends Listener {
}
public interface UsesAppendClasses extends Listener {
}
public interface UsesAppendObjects extends Listener{
}
public interface UsesPrependClasses extends Listener {
}
public interface UsesPrependObjects extends Listener {
}
public interface Unregistered {
}
public static abstract class AbstractItemNameGeneratorListener implements ItemNameGeneratorListener {
private AtomicInteger counter;
private CallbackRegistry callbackRegistry = null;
protected AbstractItemNameGeneratorListener(int startValue) {
counter = new AtomicInteger( startValue );
}
public void onGenerateItemName(ItemNameGeneratorEvent event) {
if ( event.item.name == null && getCategory() == event.item.category ) {
event.item.name = getPrefix() + counter.getAndIncrement();
}
}
public abstract Category getCategory();
public abstract String getPrefix();
@Override
public void injectCallbackRegistry(CallbackRegistry callbackRegistry) {
this.callbackRegistry = callbackRegistry;
}
@Override
public CallbackRegistry getCallbackRegistry() {
return callbackRegistry;
}
}
public static abstract class ClothingGeneratorListener extends AbstractItemNameGeneratorListener {
protected ClothingGeneratorListener() {
super( 100 );
}
@Override
public Category getCategory() {
return Category.CLOTHING;
}
@Override
public String getPrefix() {
return "C";
}
}
public static class ClothingGeneratorListenerSetClasses extends ClothingGeneratorListener implements UsesSetClasses {
}
public static class ClothingGeneratorListenerSetObjects extends ClothingGeneratorListener implements UsesSetObjects {
}
public static class ClothingGeneratorListenerAppendClasses extends ClothingGeneratorListener implements UsesAppendClasses {
}
public static class ClothingGeneratorListenerAppendObjects extends ClothingGeneratorListener implements UsesAppendObjects {
}
public static class ClothingGeneratorListenerPrependClasses extends ClothingGeneratorListener implements UsesPrependClasses {
}
public static class ClothingGeneratorListenerPrependObjects extends ClothingGeneratorListener implements UsesPrependObjects {
}
public static abstract class FurnitureGeneratorListener extends AbstractItemNameGeneratorListener {
protected FurnitureGeneratorListener() {
super( 200 );
}
@Override
public Category getCategory() {
return Category.FURNITURE;
}
@Override
public String getPrefix() {
return "F";
}
}
public static class FurnitureGeneratorListenerSetClasses extends FurnitureGeneratorListener implements UsesSetClasses {
}
public static class FurnitureGeneratorListenerSetObjects extends FurnitureGeneratorListener implements UsesSetObjects {
}
public static class FurnitureGeneratorListenerAppendClasses extends FurnitureGeneratorListener implements UsesAppendClasses {
}
public static class FurnitureGeneratorListenerAppendObjects extends FurnitureGeneratorListener implements UsesAppendObjects {
}
public static class FurnitureGeneratorListenerPrependClasses extends FurnitureGeneratorListener implements UsesPrependClasses {
}
public static class FurnitureGeneratorListenerPrependObjects extends FurnitureGeneratorListener implements UsesPrependObjects {
}
public static abstract class OtherGeneratorListener extends AbstractItemNameGeneratorListener {
protected OtherGeneratorListener() {
super( 300 );
}
@Override
public Category getCategory() {
return null;
}
@Override
public String getPrefix() {
return "O";
}
}
public static class OtherGeneratorListenerSetClasses extends OtherGeneratorListener implements UsesSetClasses {
}
public static class OtherGeneratorListenerSetObjects extends OtherGeneratorListener implements UsesSetObjects {
}
public static class OtherGeneratorListenerAppendClasses extends OtherGeneratorListener implements UsesAppendClasses {
}
public static class OtherGeneratorListenerAppendObjects extends OtherGeneratorListener implements UsesAppendObjects {
}
public static class OtherGeneratorListenerPrependClasses extends OtherGeneratorListener implements UsesPrependClasses {
}
public static class OtherGeneratorListenerPrependObjects extends OtherGeneratorListener implements UsesPrependObjects {
}
public static class TheIntegrator implements Integrator {
private EventType<UsesSetClasses> eventTypeForSetListenerClasses;
private EventType<UsesSetObjects> eventTypeForSetListenerObjects;
private EventType<UsesPrependClasses> eventTypeForPrependListenerClasses;
private EventType<UsesPrependObjects> eventTypeForPrependListenerObjects;
private EventType<UsesAppendClasses> eventTypeForAppendListenerClasses;
private EventType<UsesAppendObjects> eventTypeForAppendListenerObjects;
private EventType<Unregistered> eventTypeUnregistered;
@Override
public void integrate(
Metadata metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
eventTypeForSetListenerClasses = EventType.addCustomEventType( "eventTypeForSetListenerClasses", UsesSetClasses.class );
eventTypeForSetListenerObjects = EventType.addCustomEventType( "eventTypeForSetListenerObjects", UsesSetObjects.class );
eventTypeForPrependListenerClasses = EventType.addCustomEventType( "eventTypeForPrependListenerClasses", UsesPrependClasses.class );
eventTypeForPrependListenerObjects = EventType.addCustomEventType( "eventTypeForPrependListenerObjects", UsesPrependObjects.class );
eventTypeForAppendListenerClasses = EventType.addCustomEventType( "eventTypeForAppendListenerClasses", UsesAppendClasses.class );
eventTypeForAppendListenerObjects = EventType.addCustomEventType( "eventTypeForAppendListenerObjects", UsesAppendObjects.class );
final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
eventListenerRegistry.setListeners(
eventTypeForSetListenerClasses,
ClothingGeneratorListenerSetClasses.class,
FurnitureGeneratorListenerSetClasses.class,
OtherGeneratorListenerSetClasses.class
);
eventListenerRegistry.setListeners(
eventTypeForSetListenerObjects,
new ClothingGeneratorListenerSetObjects(),
new FurnitureGeneratorListenerSetObjects(),
new OtherGeneratorListenerSetObjects()
);
eventListenerRegistry.prependListeners(
eventTypeForPrependListenerClasses,
ClothingGeneratorListenerPrependClasses.class,
FurnitureGeneratorListenerPrependClasses.class,
OtherGeneratorListenerPrependClasses.class
);
eventListenerRegistry.prependListeners(
eventTypeForPrependListenerObjects,
new ClothingGeneratorListenerPrependObjects(),
new FurnitureGeneratorListenerPrependObjects(),
new OtherGeneratorListenerPrependObjects()
);
eventListenerRegistry.appendListeners(
eventTypeForAppendListenerClasses,
ClothingGeneratorListenerAppendClasses.class,
FurnitureGeneratorListenerAppendClasses.class,
OtherGeneratorListenerAppendClasses.class
);
eventListenerRegistry.appendListeners(
eventTypeForAppendListenerObjects,
new ClothingGeneratorListenerAppendObjects(),
new FurnitureGeneratorListenerAppendObjects(),
new OtherGeneratorListenerAppendObjects()
);
// add an EventType that does not get registered
eventTypeUnregistered = EventType.addCustomEventType( "unregistered", Unregistered.class );
}
@Override
public void disintegrate(
SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
}
public EventType<UsesSetClasses> eventTypeForSetListenerClasses() {
return eventTypeForSetListenerClasses;
}
public EventType<UsesSetObjects> eventTypeForSetListenerObjects() {
return eventTypeForSetListenerObjects;
}
public EventType<UsesPrependClasses> eventTypeForPrependListenerClasses() {
return eventTypeForPrependListenerClasses;
}
public EventType<UsesPrependObjects> eventTypeForPrependListenerObjects() {
return eventTypeForPrependListenerObjects;
}
public EventType<UsesAppendClasses> eventTypeForAppendListenerClasses() {
return eventTypeForAppendListenerClasses;
}
public EventType<UsesAppendObjects> eventTypeForAppendListenerObjects() {
return eventTypeForAppendListenerObjects;
}
public EventType<Unregistered> eventTypeUnregistered() {
return eventTypeUnregistered;
}
}
}

View File

@ -0,0 +1,149 @@
/*
* 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.test.events;
import java.util.Collection;
import java.util.Collections;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventEngine;
import org.hibernate.event.spi.EventEngineContributions;
import org.hibernate.event.spi.EventEngineContributor;
import org.hibernate.event.spi.EventType;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author Steve Ebersole
*/
@TestForIssue( jiraKey = "HHH-13890")
public class EventEngineContributionsTests extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected void configureBootstrapServiceRegistryBuilder(BootstrapServiceRegistryBuilder bsrb) {
super.configureBootstrapServiceRegistryBuilder( bsrb );
bsrb.applyClassLoaderService( new TestingClassLoaderService() );
}
@Test
public void testCustomEventAccess() {
final EventEngine eventEngine = sessionFactory().getEventEngine();
{
final EventType<SexyRxySaveListener> saveEventType = eventEngine.findRegisteredEventType( SexyRxySaveListener.EVENT_NAME );
assertThat( saveEventType, sameInstance( TheContributor.INSTANCE.saveEventType ) );
assertThat( saveEventType.isStandardEvent(), is( false ) );
final EventListenerRegistry listenerRegistry = eventEngine.getListenerRegistry();
final EventListenerGroup<SexyRxySaveListener> listenerGroup = listenerRegistry.getEventListenerGroup( saveEventType );
assertThat( listenerGroup.count(), is( 1 ) );
listenerGroup.fireEventOnEachListener( RxySaveEvent.INSTANCE, SexyRxySaveListener::doIt );
assertThat( SexyRxySaveListener.INSTANCE.didIt, is(true ) );
}
{
final EventType<SexyRxyPersistListener> persistEventType = eventEngine.findRegisteredEventType( SexyRxyPersistListener.EVENT_NAME );
assertThat( persistEventType, sameInstance( TheContributor.INSTANCE.persistEventType ) );
assertThat( persistEventType.isStandardEvent(), is( false ) );
final EventListenerRegistry listenerRegistry = eventEngine.getListenerRegistry();
final EventListenerGroup<SexyRxyPersistListener> listenerGroup = listenerRegistry.getEventListenerGroup( persistEventType );
assertThat( listenerGroup.count(), is( 1 ) );
listenerGroup.fireEventOnEachListener( RxyPersistEvent.INSTANCE, SexyRxyPersistListener::doIt );
assertThat( SexyRxyPersistListener.INSTANCE.didIt, is(true ) );
}
}
public interface SexyRxyBaseListener {
}
public static class RxySaveEvent {
public static final RxySaveEvent INSTANCE = new RxySaveEvent();
}
public static class SexyRxySaveListener implements SexyRxyBaseListener {
public static final String EVENT_NAME = "rx-save";
public static final SexyRxySaveListener INSTANCE = new SexyRxySaveListener();
private boolean didIt;
public void doIt(RxySaveEvent event) {
didIt = true;
}
}
public static class RxyPersistEvent {
public static final RxyPersistEvent INSTANCE = new RxyPersistEvent();
}
public static class SexyRxyPersistListener implements SexyRxyBaseListener {
public static final String EVENT_NAME = "rx-persist";
public static final SexyRxyPersistListener INSTANCE = new SexyRxyPersistListener();
private boolean didIt;
public void doIt(RxyPersistEvent event) {
didIt = true;
}
}
public static class TheContributor implements EventEngineContributor {
/**
* Singleton access
*/
public static final TheContributor INSTANCE = new TheContributor();
private EventType<SexyRxySaveListener> saveEventType;
private EventType<SexyRxyPersistListener> persistEventType;
@Override
public void contribute(EventEngineContributions target) {
saveEventType = target.contributeEventType(
SexyRxySaveListener.EVENT_NAME,
SexyRxySaveListener.class,
SexyRxySaveListener.INSTANCE
);
persistEventType = target.contributeEventType(
SexyRxyPersistListener.EVENT_NAME,
SexyRxyPersistListener.class
);
target.configureListeners(
persistEventType,
(group) -> group.appendListener( SexyRxyPersistListener.INSTANCE )
);
}
}
public static class TestingClassLoaderService extends ClassLoaderServiceImpl {
@Override
public <S> Collection<S> loadJavaServices(Class<S> serviceContract) {
if ( serviceContract.equals( EventEngineContributor.class ) ) {
//noinspection unchecked
return (Collection<S>) Collections.singleton( TheContributor.INSTANCE );
}
return super.loadJavaServices( serviceContract );
}
}
}

View File

@ -1,168 +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.test.events;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* Concurrency test that registering a custom EventType does not interfere with
* looking up a registered EventListenerGroup.
*
* @author Gail Badner
*/
@TestForIssue( jiraKey = "HHH-13890")
public class EventTypeListenerRegistryConcurrencyTest {
private static CoreMessageLogger LOG = CoreLogging.messageLogger( EventTypeListenerRegistryConcurrencyTest.class );
@Test
public void test() {
final TheConcurrencyIntegrator integrator = new TheConcurrencyIntegrator();
BootstrapServiceRegistry bsr = new BootstrapServiceRegistryBuilder()
.applyIntegrator( integrator )
.build();
SessionFactoryImplementor sessionFactory = null;
try {
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder( bsr ).build();
sessionFactory = (SessionFactoryImplementor) new MetadataSources( ssr )
.buildMetadata()
.getSessionFactoryBuilder()
.build();
integrator.checkResults( sessionFactory.getServiceRegistry() );
}
finally {
if ( sessionFactory != null ) {
sessionFactory.close();
}
bsr.close();
}
}
private static class TheConcurrencyIntegrator implements Integrator {
private final int NUMBER_OF_EVENT_TYPES_NEW = 10000;
private final int NUMBER_OF_THREADS = 10;
private final AtomicInteger START_VALUE = new AtomicInteger( 0 );
private final List<Exception> exceptions = new ArrayList<>();
private final Set<EventType> customEventTypes = new HashSet<>( NUMBER_OF_EVENT_TYPES_NEW );
// Capture number of "standard" event types (before adding custom event types).
private final int numberEventTypesBefore = EventType.values().size();
@Override
public void integrate(
Metadata metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
final Runnable createAndRegisterEventTypes = () -> {
for ( int i = START_VALUE.getAndIncrement();
i < NUMBER_OF_EVENT_TYPES_NEW;
i += NUMBER_OF_THREADS ) {
final EventType eventType = EventType.addCustomEventType(
"event" + i,
DummyListener.class
);
try {
eventListenerRegistry.setListeners( eventType, new DummyListener() );
eventListenerRegistry.getEventListenerGroup( eventType );
}
catch (Exception ex) {
LOG.info( ex );
exceptions.add( ex );
}
}
};
final Runnable eventListenerGroupsGetter = () -> {
while( true ) {
try {
assertNotNull( eventListenerRegistry.getEventListenerGroup( EventType.AUTO_FLUSH ) );
}
catch (Exception ex) {
exceptions.add( ex );
}
}
};
final Thread[] threadsCreateAndRegisterEventTypes = new Thread[NUMBER_OF_THREADS];
final Thread[] threadsEventListenerGroupsGetter = new Thread[NUMBER_OF_THREADS];
for ( int i = 0 ; i < NUMBER_OF_THREADS; i++ ) {
threadsCreateAndRegisterEventTypes[i] = new Thread( createAndRegisterEventTypes );
threadsEventListenerGroupsGetter[i] = new Thread( eventListenerGroupsGetter );
}
for ( int i = 0 ; i < NUMBER_OF_THREADS; i++ ) {
threadsCreateAndRegisterEventTypes[i].start();
threadsEventListenerGroupsGetter[i].start();
}
try {
for ( int i = 0; i < NUMBER_OF_THREADS; i++ ) {
threadsCreateAndRegisterEventTypes[i].join();
threadsEventListenerGroupsGetter[i].interrupt();
}
}
catch (InterruptedException ex) {
LOG.info( ex );
exceptions.add( ex );
}
}
@Override
public void disintegrate(
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
}
public void checkResults(ServiceRegistry serviceRegistry) {
LOG.info( exceptions );
assertTrue( exceptions.isEmpty() );
assertEquals( numberEventTypesBefore + NUMBER_OF_EVENT_TYPES_NEW, EventType.values().size() );
final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
for ( EventType eventType : customEventTypes) {
final EventListenerGroup eventListenerGroup = eventListenerRegistry.getEventListenerGroup( eventType );
final Iterator iterator = eventListenerGroup.listeners().iterator();
assertTrue( iterator.hasNext() );
assertTrue( DummyListener.class.isInstance( iterator.next() ) );
assertFalse( iterator.hasNext() );
}
}
}
private static class DummyListener {
}
}