light refactoring of EventEngine and CallbacksFactory

This commit is contained in:
Gavin King 2024-11-23 22:03:57 +01:00
parent 9442f4da19
commit ddeaffc917
2 changed files with 130 additions and 123 deletions

View File

@ -5,7 +5,6 @@
package org.hibernate.event.spi; package org.hibernate.event.spi;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -13,15 +12,19 @@ import java.util.function.Consumer;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.service.internal.EventListenerRegistryImpl; import org.hibernate.event.service.internal.EventListenerRegistryImpl;
import org.hibernate.event.service.spi.EventListenerGroup; import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.jpa.event.internal.CallbacksFactory;
import org.hibernate.jpa.event.spi.CallbackRegistry; import org.hibernate.jpa.event.spi.CallbackRegistry;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.service.spi.Stoppable; import org.hibernate.service.spi.Stoppable;
import static java.util.Collections.unmodifiableMap;
import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty;
import static org.hibernate.jpa.event.internal.CallbacksFactory.buildCallbackRegistry;
/** /**
* Composite for the things related to Hibernate's event system. * Composite for the things related to Hibernate's event system.
* *
@ -36,102 +39,39 @@ public class EventEngine {
public EventEngine(MetadataImplementor mappings, SessionFactoryImplementor sessionFactory) { public EventEngine(MetadataImplementor mappings, SessionFactoryImplementor sessionFactory) {
final SessionFactoryOptions sessionFactoryOptions = sessionFactory.getSessionFactoryOptions();
final ServiceRegistryImplementor serviceRegistry = sessionFactory.getServiceRegistry();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// resolve (JPA) callback handlers // resolve (JPA) callback handlers
callbackRegistry = CallbacksFactory.buildCallbackRegistry( sessionFactory.getSessionFactoryOptions(), callbackRegistry = buildCallbackRegistry( sessionFactoryOptions, serviceRegistry, mappings.getEntityBindings() );
sessionFactory.getServiceRegistry(), mappings.getEntityBindings() );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// resolve event types and listeners // resolve event types and listeners
final EventListenerRegistryImpl.Builder listenerRegistryBuilder = new EventListenerRegistryImpl.Builder( final EventListenerRegistryImpl.Builder listenerRegistryBuilder =
callbackRegistry, new EventListenerRegistryImpl.Builder( callbackRegistry, sessionFactoryOptions.isJpaBootstrap() );
sessionFactory.getSessionFactoryOptions().isJpaBootstrap()
);
final Map<String,EventType<?>> eventTypes = new HashMap<>(); final Map<String,EventType<?>> eventTypes = new HashMap<>();
EventType.registerStandardTypes( eventTypes ); EventType.registerStandardTypes( eventTypes );
final EventEngineContributions contributionManager = new EventEngineContributions() { callContributors( serviceRegistry, new ContributionManager( eventTypes, listenerRegistryBuilder ) );
@Override
public <T> EventType<T> findEventType(String name) {
//noinspection unchecked
return (EventType<T>) eventTypes.get( name );
}
@Override registeredEventTypes = unmodifiableMap( eventTypes );
public <T> EventType<T> contributeEventType(String name, Class<T> listenerRole) { listenerRegistry = listenerRegistryBuilder.buildRegistry( registeredEventTypes );
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 @SafeVarargs
public final <T> EventType<T> contributeEventType(String name, Class<T> listenerRole, T... defaultListeners) {
final EventType<T> eventType = contributeEventType( name, listenerRole );
if ( defaultListeners != null ) {
listenerRegistryBuilder.getListenerGroup( eventType ).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 ) );
}
};
private static void callContributors(
ServiceRegistryImplementor serviceRegistry, EventEngineContributions contributionManager) {
final Collection<EventEngineContributor> discoveredContributors = final Collection<EventEngineContributor> discoveredContributors =
sessionFactory.getServiceRegistry() serviceRegistry.requireService( ClassLoaderService.class )
.requireService( ClassLoaderService.class )
.loadJavaServices( EventEngineContributor.class ); .loadJavaServices( EventEngineContributor.class );
if ( CollectionHelper.isNotEmpty( discoveredContributors ) ) { if ( isNotEmpty( discoveredContributors ) ) {
for ( EventEngineContributor contributor : discoveredContributors ) { for ( EventEngineContributor contributor : discoveredContributors ) {
contributor.contribute( contributionManager ); contributor.contribute( contributionManager );
} }
} }
this.registeredEventTypes = Collections.unmodifiableMap( eventTypes );
this.listenerRegistry = listenerRegistryBuilder.buildRegistry( registeredEventTypes );
} }
public Collection<EventType<?>> getRegisteredEventTypes() { public Collection<EventType<?>> getRegisteredEventTypes() {
@ -152,10 +92,73 @@ public class EventEngine {
} }
public void stop() { public void stop() {
if ( listenerRegistry instanceof Stoppable ) { if ( listenerRegistry instanceof Stoppable stoppable ) {
( (Stoppable) listenerRegistry ).stop(); stoppable.stop();
} }
callbackRegistry.release(); callbackRegistry.release();
} }
private static class ContributionManager implements EventEngineContributions {
private final Map<String, EventType<?>> eventTypes;
private final EventListenerRegistryImpl.Builder listenerRegistryBuilder;
public ContributionManager(
Map<String, EventType<?>> eventTypes,
EventListenerRegistryImpl.Builder listenerRegistryBuilder) {
this.eventTypes = eventTypes;
this.listenerRegistryBuilder = listenerRegistryBuilder;
}
@Override
public <T> EventType<T> findEventType(String name) {
//noinspection unchecked
return (EventType<T>) 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." );
}
else if ( listenerRole == null ) {
throw new HibernateException( "Custom event-type listener role must be non-null." );
}
// make sure it does not match an existing name...
else if ( eventTypes.containsKey( name ) ) {
final EventType<?> existing = eventTypes.get( name );
throw new HibernateException(
"Custom event-type already registered: " + name + " => " + existing
);
}
else {
final EventType<T> eventType = EventType.create( name, listenerRole, eventTypes.size() );
eventTypes.put( name, eventType );
return eventType;
}
}
@Override
@SafeVarargs
public final <T> EventType<T> contributeEventType(String name, Class<T> listenerRole, T... defaultListeners) {
final EventType<T> eventType = contributeEventType( name, listenerRole );
if ( defaultListeners != null ) {
listenerRegistryBuilder.getListenerGroup( eventType ).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 ) );
}
}
} }

View File

@ -29,63 +29,67 @@ import org.jboss.logging.Logger;
public final class CallbacksFactory { public final class CallbacksFactory {
private static final Logger log = Logger.getLogger( CallbacksFactory.class ); private static final Logger log = Logger.getLogger( CallbacksFactory.class );
public static CallbackRegistry buildCallbackRegistry(SessionFactoryOptions options, ServiceRegistry serviceRegistry, Collection<PersistentClass> entityBindings) { public static CallbackRegistry buildCallbackRegistry(
if ( !jpaCallBacksEnabled( options ) ) { SessionFactoryOptions options, ServiceRegistry serviceRegistry, Collection<PersistentClass> entityBindings) {
if ( !options.areJPACallbacksEnabled() ) {
return new EmptyCallbackRegistryImpl(); return new EmptyCallbackRegistryImpl();
} }
ManagedBeanRegistry beanRegistry = serviceRegistry.getService( ManagedBeanRegistry.class ); final ManagedBeanRegistry beanRegistry = serviceRegistry.getService( ManagedBeanRegistry.class );
CallbackRegistryImpl.Builder registryBuilder = new CallbackRegistryImpl.Builder(); final CallbackRegistryImpl.Builder registryBuilder = new CallbackRegistryImpl.Builder();
Set<Class<?>> entityClasses = new HashSet<>(); final Set<Class<?>> entityClasses = new HashSet<>();
for ( PersistentClass persistentClass : entityBindings ) { for ( PersistentClass persistentClass : entityBindings ) {
if ( persistentClass.getClassName() == null ) { if ( persistentClass.getClassName() != null ) {
// we can have dynamic (non-java class) mapping final Class<?> entityClass = persistentClass.getMappedClass();
continue; if ( !entityClasses.add( entityClass ) ) {
} // this most likely means we have a class mapped multiple
// times using the hbm.xml "entity name" feature
Class<?> entityClass = persistentClass.getMappedClass(); if ( log.isDebugEnabled() ) {
log.debugf(
if ( !entityClasses.add( entityClass ) ) { "Class [%s] already has callbacks registered; " +
// this most likely means we have a class mapped multiple times using the hbm.xml "assuming this means the class was mapped twice " +
// "entity name" feature "(using hbm.xml entity-name support) - skipping subsequent registrations" +
if ( log.isDebugEnabled() ) { "to avoid duplicates",
log.debugf( entityClass.getName()
"Class [%s] already has callbacks registered; " + );
"assuming this means the class was mapped twice " + }
"(using hbm.xml entity-name support) - skipping subsequent registrations" + }
"to avoid duplicates", else {
entityClass.getName() registerAllCallbacks( persistentClass, registryBuilder, entityClass, beanRegistry );
);
} }
continue;
}
registryBuilder.registerCallbacks( persistentClass.getMappedClass(),
buildCallbacks( persistentClass.getCallbackDefinitions(), beanRegistry ) );
for ( Property property : persistentClass.getDeclaredProperties() ) {
registryBuilder.registerCallbacks( persistentClass.getMappedClass(),
buildCallbacks( property.getCallbackDefinitions(), beanRegistry ) );
} }
// else we can have dynamic (non-java class) mapping
} }
return registryBuilder.build(); return registryBuilder.build();
} }
private static void registerAllCallbacks(
PersistentClass persistentClass,
CallbackRegistryImpl.Builder registryBuilder,
Class<?> entityClass,
ManagedBeanRegistry beanRegistry) {
registryBuilder.registerCallbacks( entityClass,
buildCallbacks( persistentClass.getCallbackDefinitions(), beanRegistry ) );
for ( Property property : persistentClass.getDeclaredProperties() ) {
registryBuilder.registerCallbacks( entityClass,
buildCallbacks( property.getCallbackDefinitions(), beanRegistry ) );
}
}
private static Callback[] buildCallbacks(List<CallbackDefinition> callbackDefinitions, private static Callback[] buildCallbacks(List<CallbackDefinition> callbackDefinitions,
ManagedBeanRegistry beanRegistry) { ManagedBeanRegistry beanRegistry) {
if ( callbackDefinitions == null || callbackDefinitions.isEmpty() ) { if ( callbackDefinitions == null || callbackDefinitions.isEmpty() ) {
return null; return null;
} }
List<Callback> callbacks = new ArrayList<>(); else {
for ( CallbackDefinition definition : callbackDefinitions ) { final List<Callback> callbacks = new ArrayList<>();
callbacks.add( definition.createCallback( beanRegistry ) ); for ( CallbackDefinition definition : callbackDefinitions ) {
callbacks.add( definition.createCallback( beanRegistry ) );
}
return callbacks.toArray( new Callback[0] );
} }
return callbacks.toArray( new Callback[0] );
}
private static boolean jpaCallBacksEnabled(SessionFactoryOptions options) {
return options.areJPACallbacksEnabled();
} }
} }