From cced0ad5686a78347f0d8154aa6f5f5e232a59e3 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Fri, 5 Oct 2018 15:57:49 +0100 Subject: [PATCH] HHH-13015 Optimise loading of EntityCopyObserver implementation --- .../internal/StrategySelectorBuilder.java | 19 ---- .../internal/DefaultMergeEventListener.java | 25 +---- .../EntityCopyAllowedLoggedObserver.java | 16 +-- .../internal/EntityCopyAllowedObserver.java | 13 ++- .../EntityCopyNotAllowedObserver.java | 10 +- .../EntityCopyObserverFactoryInitiator.java | 97 +++++++++++++++++++ .../event/spi/EntityCopyObserverFactory.java | 14 +++ .../service/StandardServiceInitiators.java | 2 + 8 files changed, 144 insertions(+), 52 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyObserverFactoryInitiator.java create mode 100644 hibernate-core/src/main/java/org/hibernate/event/spi/EntityCopyObserverFactory.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java index e882125688..4ddf9c3c41 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java @@ -166,7 +166,6 @@ public class StrategySelectorBuilder { addJtaPlatforms( strategySelector ); addTransactionCoordinatorBuilders( strategySelector ); addMultiTableBulkIdStrategies( strategySelector ); - addEntityCopyObserverStrategies( strategySelector ); addImplicitNamingStrategies( strategySelector ); addCacheKeysFactories( strategySelector ); @@ -429,24 +428,6 @@ public class StrategySelectorBuilder { ); } - private void addEntityCopyObserverStrategies(StrategySelectorImpl strategySelector) { - strategySelector.registerStrategyImplementor( - EntityCopyObserver.class, - EntityCopyNotAllowedObserver.SHORT_NAME, - EntityCopyNotAllowedObserver.class - ); - strategySelector.registerStrategyImplementor( - EntityCopyObserver.class, - EntityCopyAllowedObserver.SHORT_NAME, - EntityCopyAllowedObserver.class - ); - strategySelector.registerStrategyImplementor( - EntityCopyObserver.class, - EntityCopyAllowedLoggedObserver.SHORT_NAME, - EntityCopyAllowedLoggedObserver.class - ); - } - private void addImplicitNamingStrategies(StrategySelectorImpl strategySelector) { strategySelector.registerStrategyImplementor( ImplicitNamingStrategy.class, diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java index 7f587cb585..e120c87be9 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java @@ -14,9 +14,6 @@ import org.hibernate.HibernateException; import org.hibernate.ObjectDeletedException; import org.hibernate.StaleObjectStateException; import org.hibernate.WrongClassException; -import org.hibernate.boot.registry.selector.spi.StrategySelector; -import org.hibernate.cfg.AvailableSettings; -import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.internal.CascadePoint; import org.hibernate.engine.spi.CascadingAction; @@ -27,6 +24,7 @@ import org.hibernate.engine.spi.SelfDirtinessTracker; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.event.spi.EntityCopyObserver; +import org.hibernate.event.spi.EntityCopyObserverFactory; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.MergeEvent; import org.hibernate.event.spi.MergeEventListener; @@ -48,8 +46,6 @@ import org.hibernate.type.TypeHelper; public class DefaultMergeEventListener extends AbstractSaveEventListener implements MergeEventListener { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( DefaultMergeEventListener.class ); - private String entityCopyObserverStrategy; - @Override protected Map getMergeMap(Object anything) { return ( (MergeContext) anything ).invertMap(); @@ -77,23 +73,8 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme private EntityCopyObserver createEntityCopyObserver(SessionFactoryImplementor sessionFactory) { final ServiceRegistry serviceRegistry = sessionFactory.getServiceRegistry(); - if ( entityCopyObserverStrategy == null ) { - final ConfigurationService configurationService - = serviceRegistry.getService( ConfigurationService.class ); - entityCopyObserverStrategy = configurationService.getSetting( - AvailableSettings.MERGE_ENTITY_COPY_OBSERVER, - new ConfigurationService.Converter() { - @Override - public String convert(Object value) { - return value.toString(); - } - }, - EntityCopyNotAllowedObserver.SHORT_NAME - ); - LOG.debugf( "EntityCopyObserver strategy: %s", entityCopyObserverStrategy ); - } - final StrategySelector strategySelector = serviceRegistry.getService( StrategySelector.class ); - return strategySelector.resolveStrategy( EntityCopyObserver.class, entityCopyObserverStrategy ); + final EntityCopyObserverFactory configurationService = serviceRegistry.getService( EntityCopyObserverFactory.class ); + return configurationService.createEntityCopyObserver(); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyAllowedLoggedObserver.java b/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyAllowedLoggedObserver.java index f104117fde..4f83cb302a 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyAllowedLoggedObserver.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyAllowedLoggedObserver.java @@ -11,6 +11,8 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; +import org.hibernate.event.spi.EntityCopyObserver; +import org.hibernate.event.spi.EntityCopyObserverFactory; import org.hibernate.event.spi.EventSource; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; @@ -24,9 +26,12 @@ import org.hibernate.pretty.MessageHelper; * * @author Gail Badner */ -public class EntityCopyAllowedLoggedObserver extends EntityCopyAllowedObserver { +public final class EntityCopyAllowedLoggedObserver implements EntityCopyObserver { + private static final CoreMessageLogger LOG = CoreLogging.messageLogger( EntityCopyAllowedLoggedObserver.class ); + public static final EntityCopyObserverFactory FACTORY_OF_SELF = () -> new EntityCopyAllowedLoggedObserver(); + public static final String SHORT_NAME = "log"; // Tracks the number of entity copies per entity name. @@ -39,13 +44,8 @@ public class EntityCopyAllowedLoggedObserver extends EntityCopyAllowedObserver { // key is the managed entity; // value is the set of representations being merged corresponding to the same managed result. - /** - * Indicates if DEBUG logging is enabled. - * - * @return true, if DEBUG logging is enabled. - */ - public static boolean isDebugLoggingEnabled() { - return LOG.isDebugEnabled(); + private EntityCopyAllowedLoggedObserver() { + //Not to be constructed directly; use FACTORY_OF_SELF. } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyAllowedObserver.java b/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyAllowedObserver.java index 67be8c3abc..92a26b0e4e 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyAllowedObserver.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyAllowedObserver.java @@ -7,6 +7,7 @@ package org.hibernate.event.internal; import org.hibernate.event.spi.EntityCopyObserver; +import org.hibernate.event.spi.EntityCopyObserverFactory; import org.hibernate.event.spi.EventSource; /** @@ -15,9 +16,17 @@ import org.hibernate.event.spi.EventSource; * * @author Gail Badner */ -public class EntityCopyAllowedObserver implements EntityCopyObserver { +public final class EntityCopyAllowedObserver implements EntityCopyObserver { public static final String SHORT_NAME = "allow"; + private static final EntityCopyObserver INSTANCE = new EntityCopyAllowedObserver(); + + //This implementation of EntityCopyObserver is stateless, so no need to create multiple copies: + public static final EntityCopyObserverFactory FACTORY_OF_SELF = () -> INSTANCE; + + private EntityCopyAllowedObserver() { + //Not to be constructed; use INSTANCE. + } @Override public void entityCopyDetected( @@ -32,9 +41,9 @@ public class EntityCopyAllowedObserver implements EntityCopyObserver { // do nothing. } - @Override public void topLevelMergeComplete(EventSource session) { // do nothing. } + } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyNotAllowedObserver.java b/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyNotAllowedObserver.java index 9b272c7c02..af6740ef99 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyNotAllowedObserver.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyNotAllowedObserver.java @@ -8,15 +8,23 @@ package org.hibernate.event.internal; import org.hibernate.AssertionFailure; import org.hibernate.event.spi.EntityCopyObserver; +import org.hibernate.event.spi.EntityCopyObserverFactory; import org.hibernate.event.spi.EventSource; import org.hibernate.pretty.MessageHelper; /** * @author Gail Badner */ -public class EntityCopyNotAllowedObserver implements EntityCopyObserver { +public final class EntityCopyNotAllowedObserver implements EntityCopyObserver { public static final String SHORT_NAME = "disallow"; + private static final EntityCopyNotAllowedObserver INSTANCE = new EntityCopyNotAllowedObserver(); + //This implementation of EntityCopyObserver is stateless, so no need to create multiple copies: + public static final EntityCopyObserverFactory FACTORY_OF_SELF = () -> INSTANCE; + + private EntityCopyNotAllowedObserver() { + //Not to be constructed; use INSTANCE. + } @Override public void entityCopyDetected( diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyObserverFactoryInitiator.java b/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyObserverFactoryInitiator.java new file mode 100644 index 0000000000..eb3156da3e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/EntityCopyObserverFactoryInitiator.java @@ -0,0 +1,97 @@ +/* + * 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 . + */ +package org.hibernate.event.internal; + +import java.util.Map; + +import org.hibernate.HibernateException; +import org.hibernate.boot.registry.StandardServiceInitiator; +import org.hibernate.boot.registry.selector.spi.StrategySelector; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.event.spi.EntityCopyObserver; +import org.hibernate.event.spi.EntityCopyObserverFactory; +import org.hibernate.internal.CoreLogging; +import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.service.spi.ServiceRegistryImplementor; + +/** + * Looks for the configuration property {@link AvailableSettings#MERGE_ENTITY_COPY_OBSERVER} and registers + * the matching {@link EntityCopyObserverFactory} based on the configuration value. + *

+ * For known implementations some optimisations are possible, such as reusing a singleton for the stateless + * implementations. When a user plugs in a custom {@link EntityCopyObserver} we take a defensive approach. + *

+ */ +public class EntityCopyObserverFactoryInitiator implements StandardServiceInitiator { + + public static final EntityCopyObserverFactoryInitiator INSTANCE = new EntityCopyObserverFactoryInitiator(); + private static final CoreMessageLogger LOG = CoreLogging.messageLogger( EntityCopyObserverFactoryInitiator.class ); + + @Override + public EntityCopyObserverFactory initiateService(final Map configurationValues, final ServiceRegistryImplementor registry) { + final Object value = getConfigurationValue( configurationValues ); + if ( value.equals( EntityCopyNotAllowedObserver.SHORT_NAME ) || value.equals( EntityCopyNotAllowedObserver.class.getName() ) ) { + LOG.debugf( "Configured EntityCopyObserver strategy: " + EntityCopyNotAllowedObserver.SHORT_NAME ); + return EntityCopyNotAllowedObserver.FACTORY_OF_SELF; + } + else if ( value.equals( EntityCopyAllowedObserver.SHORT_NAME ) || value.equals( EntityCopyAllowedObserver.class.getName() ) ) { + LOG.debugf( "Configured EntityCopyObserver strategy: " + EntityCopyAllowedObserver.SHORT_NAME ); + return EntityCopyAllowedObserver.FACTORY_OF_SELF; + } + else if ( value.equals( EntityCopyAllowedLoggedObserver.SHORT_NAME ) || value.equals( EntityCopyAllowedLoggedObserver.class.getName() ) ) { + LOG.debugf( "Configured EntityCopyObserver strategy: " + EntityCopyAllowedLoggedObserver.SHORT_NAME ); + return EntityCopyAllowedLoggedObserver.FACTORY_OF_SELF; + } + else { + //We load an "example instance" just to get its Class; + //this might look excessive, but it also happens to test eagerly (at boot) that we can actually construct these + //and that they are indeed of the right type. + EntityCopyObserver exampleInstance = registry.getService( StrategySelector.class ).resolveStrategy( EntityCopyObserver.class, value ); + Class observerType = exampleInstance.getClass(); + LOG.debugf( "Configured EntityCopyObserver is a custom implementation of type " + observerType.getName() ); + return new EntityObserversFactoryFromClass( observerType ); + } + } + + private Object getConfigurationValue(final Map configurationValues) { + final Object o = configurationValues.get( AvailableSettings.MERGE_ENTITY_COPY_OBSERVER ); + if ( o == null ) { + return EntityCopyNotAllowedObserver.SHORT_NAME; //default + } + else if ( o instanceof String ) { + return o.toString().trim(); + } + else { + return o; + } + } + + @Override + public Class getServiceInitiated() { + return EntityCopyObserverFactory.class; + } + + private static class EntityObserversFactoryFromClass implements EntityCopyObserverFactory { + + private final Class value; + + public EntityObserversFactoryFromClass(Class value) { + this.value = value; + } + + @Override + public EntityCopyObserver createEntityCopyObserver() { + try { + return (EntityCopyObserver) value.newInstance(); + } + catch (Exception e) { + throw new HibernateException( "Could not instantiate class of type " + value.getName() ); + } + } + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/EntityCopyObserverFactory.java b/hibernate-core/src/main/java/org/hibernate/event/spi/EntityCopyObserverFactory.java new file mode 100644 index 0000000000..f2752a8eb2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/EntityCopyObserverFactory.java @@ -0,0 +1,14 @@ +/* + * 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 . + */ +package org.hibernate.event.spi; + +import org.hibernate.service.Service; + +@FunctionalInterface +public interface EntityCopyObserverFactory extends Service { + EntityCopyObserver createEntityCopyObserver(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/service/StandardServiceInitiators.java b/hibernate-core/src/main/java/org/hibernate/service/StandardServiceInitiators.java index 0349132c22..0eedc5624d 100644 --- a/hibernate-core/src/main/java/org/hibernate/service/StandardServiceInitiators.java +++ b/hibernate-core/src/main/java/org/hibernate/service/StandardServiceInitiators.java @@ -25,6 +25,7 @@ import org.hibernate.engine.jdbc.internal.JdbcServicesInitiator; import org.hibernate.engine.jndi.internal.JndiServiceInitiator; import org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator; import org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformResolverInitiator; +import org.hibernate.event.internal.EntityCopyObserverFactoryInitiator; import org.hibernate.hql.internal.QueryTranslatorFactoryInitiator; import org.hibernate.id.factory.internal.MutableIdentifierGeneratorFactoryInitiator; import org.hibernate.jmx.internal.JmxServiceInitiator; @@ -86,6 +87,7 @@ public final class StandardServiceInitiators { serviceInitiators.add( TransactionCoordinatorBuilderInitiator.INSTANCE ); serviceInitiators.add( ManagedBeanRegistryInitiator.INSTANCE ); + serviceInitiators.add( EntityCopyObserverFactoryInitiator.INSTANCE ); serviceInitiators.trimToSize();