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;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
@ -13,15 +12,19 @@ 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.boot.spi.SessionFactoryOptions;
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.CallbacksFactory;
import org.hibernate.jpa.event.spi.CallbackRegistry;
import org.hibernate.service.spi.ServiceRegistryImplementor;
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.
*
@ -36,102 +39,39 @@ public class EventEngine {
public EventEngine(MetadataImplementor mappings, SessionFactoryImplementor sessionFactory) {
final SessionFactoryOptions sessionFactoryOptions = sessionFactory.getSessionFactoryOptions();
final ServiceRegistryImplementor serviceRegistry = sessionFactory.getServiceRegistry();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// resolve (JPA) callback handlers
callbackRegistry = CallbacksFactory.buildCallbackRegistry( sessionFactory.getSessionFactoryOptions(),
sessionFactory.getServiceRegistry(), mappings.getEntityBindings() );
callbackRegistry = buildCallbackRegistry( sessionFactoryOptions, serviceRegistry, mappings.getEntityBindings() );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// resolve event types and listeners
final EventListenerRegistryImpl.Builder listenerRegistryBuilder = new EventListenerRegistryImpl.Builder(
callbackRegistry,
sessionFactory.getSessionFactoryOptions().isJpaBootstrap()
);
final EventListenerRegistryImpl.Builder listenerRegistryBuilder =
new EventListenerRegistryImpl.Builder( callbackRegistry, sessionFactoryOptions.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 (EventType<T>) eventTypes.get( name );
}
callContributors( serviceRegistry, new ContributionManager( eventTypes, listenerRegistryBuilder ) );
@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 @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 ) );
}
};
registeredEventTypes = unmodifiableMap( eventTypes );
listenerRegistry = listenerRegistryBuilder.buildRegistry( registeredEventTypes );
}
private static void callContributors(
ServiceRegistryImplementor serviceRegistry, EventEngineContributions contributionManager) {
final Collection<EventEngineContributor> discoveredContributors =
sessionFactory.getServiceRegistry()
.requireService( ClassLoaderService.class )
serviceRegistry.requireService( ClassLoaderService.class )
.loadJavaServices( EventEngineContributor.class );
if ( CollectionHelper.isNotEmpty( discoveredContributors ) ) {
if ( isNotEmpty( discoveredContributors ) ) {
for ( EventEngineContributor contributor : discoveredContributors ) {
contributor.contribute( contributionManager );
}
}
this.registeredEventTypes = Collections.unmodifiableMap( eventTypes );
this.listenerRegistry = listenerRegistryBuilder.buildRegistry( registeredEventTypes );
}
public Collection<EventType<?>> getRegisteredEventTypes() {
@ -152,10 +92,73 @@ public class EventEngine {
}
public void stop() {
if ( listenerRegistry instanceof Stoppable ) {
( (Stoppable) listenerRegistry ).stop();
if ( listenerRegistry instanceof Stoppable stoppable ) {
stoppable.stop();
}
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 {
private static final Logger log = Logger.getLogger( CallbacksFactory.class );
public static CallbackRegistry buildCallbackRegistry(SessionFactoryOptions options, ServiceRegistry serviceRegistry, Collection<PersistentClass> entityBindings) {
if ( !jpaCallBacksEnabled( options ) ) {
public static CallbackRegistry buildCallbackRegistry(
SessionFactoryOptions options, ServiceRegistry serviceRegistry, Collection<PersistentClass> entityBindings) {
if ( !options.areJPACallbacksEnabled() ) {
return new EmptyCallbackRegistryImpl();
}
ManagedBeanRegistry beanRegistry = serviceRegistry.getService( ManagedBeanRegistry.class );
CallbackRegistryImpl.Builder registryBuilder = new CallbackRegistryImpl.Builder();
Set<Class<?>> entityClasses = new HashSet<>();
final ManagedBeanRegistry beanRegistry = serviceRegistry.getService( ManagedBeanRegistry.class );
final CallbackRegistryImpl.Builder registryBuilder = new CallbackRegistryImpl.Builder();
final Set<Class<?>> entityClasses = new HashSet<>();
for ( PersistentClass persistentClass : entityBindings ) {
if ( persistentClass.getClassName() == null ) {
// we can have dynamic (non-java class) mapping
continue;
}
Class<?> entityClass = persistentClass.getMappedClass();
if ( !entityClasses.add( entityClass ) ) {
// this most likely means we have a class mapped multiple times using the hbm.xml
// "entity name" feature
if ( log.isDebugEnabled() ) {
log.debugf(
"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",
entityClass.getName()
);
if ( persistentClass.getClassName() != null ) {
final Class<?> entityClass = persistentClass.getMappedClass();
if ( !entityClasses.add( entityClass ) ) {
// this most likely means we have a class mapped multiple
// times using the hbm.xml "entity name" feature
if ( log.isDebugEnabled() ) {
log.debugf(
"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",
entityClass.getName()
);
}
}
else {
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();
}
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,
ManagedBeanRegistry beanRegistry) {
if ( callbackDefinitions == null || callbackDefinitions.isEmpty() ) {
return null;
}
List<Callback> callbacks = new ArrayList<>();
for ( CallbackDefinition definition : callbackDefinitions ) {
callbacks.add( definition.createCallback( beanRegistry ) );
else {
final List<Callback> callbacks = new ArrayList<>();
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();
}
}