diff --git a/gradle/java-module.gradle b/gradle/java-module.gradle index e9846a72aa..e7e61ec73f 100644 --- a/gradle/java-module.gradle +++ b/gradle/java-module.gradle @@ -368,6 +368,10 @@ task forbiddenApisSystemOut(type: CheckForbiddenApis, dependsOn: compileJava) { targetCompatibility = project.forbiddenAPITargetJDKCompatibility bundledSignatures += 'jdk-system-out' suppressAnnotations += ['org.hibernate.internal.build.AllowSysOut', 'org.hibernate.internal.build.AllowPrintStacktrace'] + + // This slows down the checks a little, but is necessary to avoid the gradle deamon holding on + // to class definitions loaded previously - even possibly in a previous build. + disableClassloadingCache = true } task forbiddenApisUnsafe(type: CheckForbiddenApis, dependsOn: compileJava) { @@ -381,6 +385,10 @@ task forbiddenApisUnsafe(type: CheckForbiddenApis, dependsOn: compileJava) { // // No idea how findbugs was missing these b4 ignoreFailures = true + + // This slows down the checks a little, but is necessary to avoid the gradle deamon holding on + // to class definitions loaded previously - even possibly in a previous build. + disableClassloadingCache = true } task forbiddenApisNonPortable(type: CheckForbiddenApis, dependsOn: compileJava) { @@ -388,6 +396,10 @@ task forbiddenApisNonPortable(type: CheckForbiddenApis, dependsOn: compileJava) classpath = project.sourceSets.main.compileClasspath + project.sourceSets.main.runtimeClasspath targetCompatibility = project.forbiddenAPITargetJDKCompatibility bundledSignatures += 'jdk-non-portable' + + // This slows down the checks a little, but is necessary to avoid the gradle deamon holding on + // to class definitions loaded previously - even possibly in a previous build. + disableClassloadingCache = true } task forbiddenApis diff --git a/gradle/libraries.gradle b/gradle/libraries.gradle index 0ab904514d..c9108e0e79 100644 --- a/gradle/libraries.gradle +++ b/gradle/libraries.gradle @@ -26,9 +26,9 @@ ext { weldVersion = '3.0.0.Final' javassistVersion = '3.24.0-GA' - byteBuddyVersion = '1.10.7' + byteBuddyVersion = '1.10.10' - agroalVersion = '1.7' + agroalVersion = '1.8' assertjVersion = '3.14.0' @@ -59,7 +59,7 @@ ext { // Annotations commons_annotations: "org.hibernate.common:hibernate-commons-annotations:${hibernateCommonsVersion}", - jandex: 'org.jboss:jandex:2.1.1.Final', + jandex: 'org.jboss:jandex:2.1.3.Final', classmate: 'com.fasterxml:classmate:1.5.1', // Dom4J diff --git a/hibernate-core/src/main/java/org/hibernate/ConnectionReleaseMode.java b/hibernate-core/src/main/java/org/hibernate/ConnectionReleaseMode.java index 80f96245d8..443cf856ca 100644 --- a/hibernate-core/src/main/java/org/hibernate/ConnectionReleaseMode.java +++ b/hibernate-core/src/main/java/org/hibernate/ConnectionReleaseMode.java @@ -25,6 +25,13 @@ public enum ConnectionReleaseMode{ */ AFTER_STATEMENT, + /** + * Indicates that JDBC connections should be released before each transaction + * commits/rollbacks (works with both JTA-registered synch and HibernateTransaction API). + * This mode may be used with an application server JTA datasource. + */ + BEFORE_TRANSACTION_COMPLETION, + /** * Indicates that JDBC connections should be released after each transaction * ends (works with both JTA-registered synch and HibernateTransaction API). diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataImpl.java index d02e454b4c..0980cdac2e 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataImpl.java @@ -384,8 +384,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; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java index 177bf4ddcf..50a1f2d98e 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java @@ -48,10 +48,13 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement @SuppressWarnings("WeakerAccess") public SessionFactoryBuilderImpl(MetadataImplementor metadata, BootstrapContext bootstrapContext) { - this( metadata, new SessionFactoryOptionsBuilder( - metadata.getMetadataBuildingOptions().getServiceRegistry(), - bootstrapContext - ) ); + this( + metadata, + new SessionFactoryOptionsBuilder( + metadata.getMetadataBuildingOptions().getServiceRegistry(), + bootstrapContext + ) + ); } @SuppressWarnings("WeakerAccess") @@ -266,7 +269,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; } @@ -469,5 +472,4 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement public SessionFactoryOptions buildSessionFactoryOptions() { return optionsBuilder.buildOptions(); } - } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/DefaultDialectSelector.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/DefaultDialectSelector.java new file mode 100644 index 0000000000..23fe829016 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/DefaultDialectSelector.java @@ -0,0 +1,281 @@ +/* + * 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.boot.registry.selector.internal; + +import java.util.Objects; + +import org.hibernate.dialect.CUBRIDDialect; +import org.hibernate.dialect.Cache71Dialect; +import org.hibernate.dialect.DB2390Dialect; +import org.hibernate.dialect.DB2390V8Dialect; +import org.hibernate.dialect.DB2400Dialect; +import org.hibernate.dialect.DB2400V7R3Dialect; +import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.DerbyTenFiveDialect; +import org.hibernate.dialect.DerbyTenSevenDialect; +import org.hibernate.dialect.DerbyTenSixDialect; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.FirebirdDialect; +import org.hibernate.dialect.FrontBaseDialect; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.dialect.HANAColumnStoreDialect; +import org.hibernate.dialect.HANARowStoreDialect; +import org.hibernate.dialect.HSQLDialect; +import org.hibernate.dialect.InformixDialect; +import org.hibernate.dialect.Ingres10Dialect; +import org.hibernate.dialect.Ingres9Dialect; +import org.hibernate.dialect.IngresDialect; +import org.hibernate.dialect.InterbaseDialect; +import org.hibernate.dialect.JDataStoreDialect; +import org.hibernate.dialect.MckoiDialect; +import org.hibernate.dialect.MimerSQLDialect; +import org.hibernate.dialect.MySQL57Dialect; +import org.hibernate.dialect.MySQL57InnoDBDialect; +import org.hibernate.dialect.MySQL5Dialect; +import org.hibernate.dialect.MySQL5InnoDBDialect; +import org.hibernate.dialect.MySQL8Dialect; +import org.hibernate.dialect.Oracle10gDialect; +import org.hibernate.dialect.Oracle8iDialect; +import org.hibernate.dialect.Oracle9iDialect; +import org.hibernate.dialect.PointbaseDialect; +import org.hibernate.dialect.PostgreSQL81Dialect; +import org.hibernate.dialect.PostgreSQL82Dialect; +import org.hibernate.dialect.PostgreSQL9Dialect; +import org.hibernate.dialect.PostgresPlusDialect; +import org.hibernate.dialect.ProgressDialect; +import org.hibernate.dialect.SAPDBDialect; +import org.hibernate.dialect.SQLServer2005Dialect; +import org.hibernate.dialect.SQLServer2008Dialect; +import org.hibernate.dialect.SQLServerDialect; +import org.hibernate.dialect.Sybase11Dialect; +import org.hibernate.dialect.SybaseASE157Dialect; +import org.hibernate.dialect.SybaseASE15Dialect; +import org.hibernate.dialect.SybaseAnywhereDialect; +import org.hibernate.dialect.TeradataDialect; +import org.hibernate.dialect.TimesTenDialect; + +public class DefaultDialectSelector implements LazyServiceResolver { + + @Override + public Class resolve(final String name) { + Objects.requireNonNull( name); + if ( name.isEmpty() ) { + return null; + } + //Let's organize all string matches in groups by first letter: + final char n = name.charAt( 0 ); + switch ( n ) { + case 'C': return caseC( name ); + case 'D': return caseD( name ); + case 'F': return caseF( name ); + case 'H': return caseH( name ); + case 'I': return caseI( name ); + case 'J': return caseJ( name ); + case 'M': return caseM( name ); + case 'O': return caseO( name ); + case 'P': return caseP( name ); + case 'S': return caseS( name ); + case 'T': return caseT( name ); + } + return null; + } + + private static Class caseC(final String name) { + if ( name.equals( "Cache71" ) ) { + return Cache71Dialect.class; + } + if ( name.equals( "CUBRID" ) ) { + return CUBRIDDialect.class; + } + return null; + } + + private static Class caseD(final String name) { + if ( name.equals( "DB2" ) ) { + return DB2Dialect.class; + } + if ( name.equals( "DB2390" ) ) { + return DB2390Dialect.class; + } + if ( name.equals( "DB2390V8" ) ) { + return DB2390V8Dialect.class; + } + if ( name.equals( "DB2400" ) ) { + return DB2400Dialect.class; + } + if ( name.equals( "DB2400V7R3" ) ) { + return DB2400V7R3Dialect.class; + } + if ( name.equals( "DerbyTenFive" ) ) { + return DerbyTenFiveDialect.class; + } + if ( name.equals( "DerbyTenSix" ) ) { + return DerbyTenSixDialect.class; + } + if ( name.equals( "DerbyTenSeven" ) ) { + return DerbyTenSevenDialect.class; + } + return null; + } + + private static Class caseF(final String name) { + if ( name.equals( "Firebird" ) ) { + return FirebirdDialect.class; + } + if ( name.equals( "FrontBase" ) ) { + return FrontBaseDialect.class; + } + return null; + } + + private static Class caseH(final String name) { + if ( name.equals( "H2" ) ) { + return H2Dialect.class; + } + if ( name.equals( "HANAColumnStore" ) ) { + return HANAColumnStoreDialect.class; + } + if ( name.equals( "HANARowStore" ) ) { + return HANARowStoreDialect.class; + } + if ( name.equals( "HSQL" ) ) { + return HSQLDialect.class; + } + return null; + } + + private static Class caseI(final String name) { + if ( name.equals( "Informix" ) ) { + return InformixDialect.class; + } + if ( name.equals( "Ingres" ) ) { + return IngresDialect.class; + } + if ( name.equals( "Ingres9" ) ) { + return Ingres9Dialect.class; + } + if ( name.equals( "Ingres10" ) ) { + return Ingres10Dialect.class; + } + if ( name.equals( "Interbase" ) ) { + return InterbaseDialect.class; + } + return null; + } + + private static Class caseJ(final String name) { + if ( name.equals( "JDataStore" ) ) { + return JDataStoreDialect.class; + } + return null; + } + + private static Class caseM(final String name) { + if ( name.equals( "Mckoi" ) ) { + return MckoiDialect.class; + } + if ( name.equals( "MimerSQL" ) ) { + return MimerSQLDialect.class; + } + if ( name.equals( "MySQL5" ) ) { + return MySQL5Dialect.class; + } + if ( name.equals( "MySQL5InnoDB" ) ) { + return MySQL5InnoDBDialect.class; + } + if ( name.equals( "MySQL57InnoDB" ) ) { + return MySQL57InnoDBDialect.class; + } + if ( name.equals( "MySQL57" ) ) { + return MySQL57Dialect.class; + } + if ( name.equals( "MySQL8" ) ) { + return MySQL8Dialect.class; + } + return null; + } + + private static Class caseO(final String name) { + if ( name.equals( "Oracle8i" ) ) { + return Oracle8iDialect.class; + } + if ( name.equals( "Oracle9i" ) ) { + return Oracle9iDialect.class; + } + if ( name.equals( "Oracle10g" ) ) { + return Oracle10gDialect.class; + } + return null; + } + + private static Class caseP(final String name) { + if ( name.equals( "Pointbase" ) ) { + return PointbaseDialect.class; + } + if ( name.equals( "PostgresPlus" ) ) { + return PostgresPlusDialect.class; + } + if ( name.equals( "PostgreSQL81" ) ) { + return PostgreSQL81Dialect.class; + } + if ( name.equals( "PostgreSQL82" ) ) { + return PostgreSQL82Dialect.class; + } + if ( name.equals( "PostgreSQL9" ) ) { + return PostgreSQL9Dialect.class; + } + if ( name.equals( "Progress" ) ) { + return ProgressDialect.class; + } + return null; + } + + private static Class caseS(final String name) { + if ( name.equals( "SAPDB" ) ) { + return SAPDBDialect.class; + } + if ( name.equals( "SQLServer" ) ) { + return SQLServerDialect.class; + } + if ( name.equals( "SQLServer2005" ) ) { + return SQLServer2005Dialect.class; + } + if ( name.equals( "SQLServer2008" ) ) { + return SQLServer2008Dialect.class; + } + if ( name.equals( "Sybase11" ) ) { + return Sybase11Dialect.class; + } + if ( name.equals( "SybaseAnywhere" ) ) { + return SybaseAnywhereDialect.class; + } + if ( name.equals( "Sybase11" ) ) { + return Sybase11Dialect.class; + } + if ( name.equals( "SybaseAnywhere" ) ) { + return SybaseAnywhereDialect.class; + } + if ( name.equals( "SybaseASE15" ) ) { + return SybaseASE15Dialect.class; + } + if ( name.equals( "SybaseASE157" ) ) { + return SybaseASE157Dialect.class; + } + return null; + } + + private static Class caseT(final String name) { + if ( name.equals( "Teradata" ) ) { + return TeradataDialect.class; + } + if ( name.equals( "TimesTen" ) ) { + return TimesTenDialect.class; + } + return null; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/DefaultJtaPlatformSelector.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/DefaultJtaPlatformSelector.java new file mode 100644 index 0000000000..52274c319a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/DefaultJtaPlatformSelector.java @@ -0,0 +1,156 @@ +/* + * 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.boot.registry.selector.internal; + +import java.util.Objects; + +import org.hibernate.engine.transaction.jta.platform.internal.AtomikosJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.BitronixJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.BorlandEnterpriseServerJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.JBossAppServerJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.JBossStandAloneJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.JOTMJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.JOnASJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.JRun4JtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.OC4JJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.OrionJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.ResinJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.SapNetWeaverJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.SunOneJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.WebSphereExtendedJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.WebSphereJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.WebSphereLibertyJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.WeblogicJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; + +public class DefaultJtaPlatformSelector implements LazyServiceResolver { + + @Override + public Class resolve(final String name) { + Objects.requireNonNull( name); + if ( name.isEmpty() ) { + return null; + } + //Let's organize all string matches in groups by first letter: + final char n = name.charAt( 0 ); + switch ( n ) { + case 'B': return caseB( name ); + case 'J': return caseJ( name ); + case 'W': return caseW( name ); + case 'o': return caseLegacy( name, this ); + default: return caseOthers( name ); + } + } + + private static Class caseB(final String name) { + if ( "Bitronix".equals( name ) ) { + return BitronixJtaPlatform.class; + } + if ( "Borland".equals( name ) ) { + return BorlandEnterpriseServerJtaPlatform.class; + } + return null; + } + + private static Class caseJ(final String name) { + if ( "JBossAS".equals( name ) ) { + return JBossAppServerJtaPlatform.class; + } + if ( "JBossTS".equals( name ) ) { + return JBossStandAloneJtaPlatform.class; + } + if ( "JOnAS".equals( name ) ) { + return JOnASJtaPlatform.class; + } + if ( "JOTM".equals( name ) ) { + return JOTMJtaPlatform.class; + } + if ( "JRun4".equals( name ) ) { + return JRun4JtaPlatform.class; + } + return null; + } + + private static Class caseW(final String name) { + if ( "Weblogic".equals( name ) ) { + return WeblogicJtaPlatform.class; + } + if ( "WebSphereLiberty".equals( name ) ) { + return WebSphereLibertyJtaPlatform.class; + } + if ( "WebSphere".equals( name ) ) { + return WebSphereJtaPlatform.class; + } + if ( "WebSphereExtended".equals( name ) ) { + return WebSphereExtendedJtaPlatform.class; + } + return null; + } + + private static Class caseOthers(final String name) { + if ( "Atomikos".equals( name ) ) { + return AtomikosJtaPlatform.class; + } + if ( "OC4J".equals( name ) ) { + return OC4JJtaPlatform.class; + } + if ( "Orion".equals( name ) ) { + return OrionJtaPlatform.class; + } + if ( "Resin".equals( name ) ) { + return ResinJtaPlatform.class; + } + if ( "SapNetWeaver".equals( name ) ) { + return SapNetWeaverJtaPlatform.class; + } + if ( "SunOne".equals( name ) ) { + return SunOneJtaPlatform.class; + } + return null; + } + + /** + * Special case: we have several old fully qualified classnames which need to + * be remapped to their new names for backwards compatibility reasons. + * @param name + * @param defaultJtaPlatformSelector + * @return + */ + private static Class caseLegacy( + final String name, + final DefaultJtaPlatformSelector defaultJtaPlatformSelector) { + + //First, let's deal with the special cases which don't follow any recognizable pattern: + if ( name.equals( "org.hibernate.service.jta.platform.internal.BorlandEnterpriseServerJtaPlatform" ) ) { + return BorlandEnterpriseServerJtaPlatform.class; + } + if ( name.equals( "org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" ) ) { + return JBossAppServerJtaPlatform.class; + } + if ( name.equals( "org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform" ) ) { + return JBossStandAloneJtaPlatform.class; + } + //This one shouldn't be necessary as it matches the implementation FQN, but let's translate the existing + //code faithfully. + if ( name.equals( "org.hibernate.engine.transaction.jta.platform.internal.WebSphereLibertyJtaPlatform" ) ) { + return WebSphereLibertyJtaPlatform.class; + } + + //All other ones follow a pattern, beginning with the same prefix and ending with the same postfix, + //if your remove those the remaining section happens to match the short name. + final String LEGACY_PREFIX = "org.hibernate.service.jta.platform.internal."; + final String LEGACY_POSTFIX = "JtaPlatform"; + + //All these follow the same pattern, allowing us to use recursion into the main method: + if ( name.startsWith( LEGACY_PREFIX ) && name.endsWith( LEGACY_POSTFIX ) ) { + final String cleanName = name.substring( LEGACY_PREFIX.length(), name.length() - LEGACY_POSTFIX.length() ); + return defaultJtaPlatformSelector.resolve( cleanName ); + } + return null; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/LazyServiceResolver.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/LazyServiceResolver.java new file mode 100644 index 0000000000..43c6e171fe --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/LazyServiceResolver.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.boot.registry.selector.internal; + +@FunctionalInterface +public interface LazyServiceResolver { + + Class resolve(String name); + +} 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 e455c85f45..ea03f4dfa3 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 @@ -23,16 +23,6 @@ import org.hibernate.boot.registry.selector.spi.StrategySelector; import org.hibernate.cache.internal.DefaultCacheKeysFactory; import org.hibernate.cache.internal.SimpleCacheKeysFactory; import org.hibernate.cache.spi.CacheKeysFactory; -import org.hibernate.dialect.CUBRIDDialect; -import org.hibernate.dialect.Cache71Dialect; -import org.hibernate.dialect.DB2390Dialect; -import org.hibernate.dialect.DB2390V8Dialect; -import org.hibernate.dialect.DB2400Dialect; -import org.hibernate.dialect.DB2400V7R3Dialect; -import org.hibernate.dialect.DB2Dialect; -import org.hibernate.dialect.DerbyTenFiveDialect; -import org.hibernate.dialect.DerbyTenSevenDialect; -import org.hibernate.dialect.DerbyTenSixDialect; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.FirebirdDialect; import org.hibernate.dialect.H2Dialect; @@ -152,8 +142,8 @@ public class StrategySelectorBuilder { final StrategySelectorImpl strategySelector = new StrategySelectorImpl( classLoaderService ); // build the baseline... - addDialects( strategySelector ); - addJtaPlatforms( strategySelector ); + strategySelector.registerStrategyLazily( Dialect.class, new DefaultDialectSelector() ); + strategySelector.registerStrategyLazily( JtaPlatform.class, new DefaultJtaPlatformSelector() ); addTransactionCoordinatorBuilders( strategySelector ); addSqmMultiTableMutationStrategies( strategySelector ); addImplicitNamingStrategies( strategySelector ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorImpl.java index 9398e488d7..f0b4351257 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorImpl.java @@ -13,7 +13,9 @@ import java.util.Iterator; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import org.hibernate.HibernateException; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.boot.registry.selector.spi.StrategyCreator; @@ -28,10 +30,11 @@ import org.jboss.logging.Logger; * @author Steve Ebersole */ public class StrategySelectorImpl implements StrategySelector { + private static final Logger log = Logger.getLogger( StrategySelectorImpl.class ); @SuppressWarnings("WeakerAccess") - public static final StrategyCreator STANDARD_STRATEGY_CREATOR = strategyClass -> { + private static final StrategyCreator STANDARD_STRATEGY_CREATOR = strategyClass -> { try { return strategyClass.newInstance(); } @@ -43,8 +46,14 @@ public class StrategySelectorImpl implements StrategySelector { } }; + //Map based approach: most suited for explicit registrations from integrators private final Map> namedStrategyImplementorByStrategyMap = new ConcurrentHashMap<>(); + //"Lazy" approach: more efficient as we aim to not initialize all implementation classes; + //this is preferable for internal services such as Dialect, as we have a significant amount of them, making + //it worthwhile to try be a bit more efficient about them. + private final Map lazyStrategyImplementorByStrategyMap = new ConcurrentHashMap<>(); + private final ClassLoaderService classLoaderService; /** @@ -56,6 +65,13 @@ public class StrategySelectorImpl implements StrategySelector { this.classLoaderService = classLoaderService; } + public void registerStrategyLazily(Class strategy, LazyServiceResolver resolver) { + LazyServiceResolver previous = lazyStrategyImplementorByStrategyMap.put( strategy, resolver ); + if ( previous != null ) { + throw new HibernateException( "Detected a second LazyServiceResolver replacing an existing LazyServiceResolver implementation for strategy " + strategy.getName() ); + } + } + @Override public void registerStrategyImplementor(Class strategy, String name, Class implementation) { final Map namedStrategyImplementorMap = namedStrategyImplementorByStrategyMap.computeIfAbsent( @@ -124,6 +140,14 @@ public class StrategySelectorImpl implements StrategySelector { } } + LazyServiceResolver lazyServiceResolver = lazyStrategyImplementorByStrategyMap.get( strategy ); + if ( lazyServiceResolver != null ) { + Class resolve = lazyServiceResolver.resolve( name ); + if ( resolve != null ) { + return resolve; + } + } + try { return classLoaderService.classForName( name ); } @@ -175,6 +199,10 @@ public class StrategySelectorImpl implements StrategySelector { @Override @SuppressWarnings("unchecked") public Collection getRegisteredStrategyImplementors(Class strategy) { + LazyServiceResolver lazyServiceResolver = lazyStrategyImplementorByStrategyMap.get( strategy ); + if ( lazyServiceResolver != null ) { + throw new StrategySelectionException( "Can't use this method on for strategy types which are embedded in the core library" ); + } final Map registrations = namedStrategyImplementorByStrategyMap.get( strategy ); if ( registrations == null ) { return Collections.emptySet(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java index 7494b64e70..44249e9d2f 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java @@ -439,7 +439,8 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator { @Override public void beforeTransactionCompletion() { - owner.beforeTransactionCompletion(); + this.owner.beforeTransactionCompletion(); + this.logicalConnection.beforeTransactionCompletion(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java index 03855afaee..3e4d8223c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java @@ -35,6 +35,8 @@ import org.hibernate.dialect.Dialect; 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; @@ -143,6 +145,11 @@ public class SessionFactoryDelegatingImpl implements SessionFactoryImplementor, return delegate.getRuntimeMetamodels(); } + @Override + public EventEngine getEventEngine() { + return delegate.getEventEngine(); + } + @Override public void close() throws HibernateException { delegate.close(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java index 41685de393..09b327c6a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java @@ -27,6 +27,8 @@ import org.hibernate.dialect.Dialect; 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; @@ -113,6 +115,11 @@ public interface SessionFactoryImplementor */ ServiceRegistryImplementor getServiceRegistry(); + /** + * Get the EventEngine associated with this SessionFactory + */ + EventEngine getEventEngine(); + /** * Retrieve fetch profile by name. * diff --git a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java index c13c797b6c..ba757586d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java @@ -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 implements EventListenerGroup { private static final Logger log = Logger.getLogger( EventListenerGroupImpl.class ); private final EventType eventType; - private final EventListenerRegistryImpl listenerRegistry; + private final CallbackRegistry callbackRegistry; + private final boolean isJpaBootstrap; private final Set duplicationStrategies = new LinkedHashSet<>(); private T[] listeners = null; - public EventListenerGroupImpl(EventType eventType, EventListenerRegistryImpl listenerRegistry) { + public EventListenerGroupImpl( + EventType 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 implements EventListenerGroup { } 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 ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerRegistryImpl.java b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerRegistryImpl.java index 42963cabb2..ccd0d554cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerRegistryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerRegistryImpl.java @@ -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 listenerClassToInstanceMap = new HashMap<>(); + @SuppressWarnings("rawtypes") + private final EventListenerGroup[] eventListeners; + private final Map,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 EventListenerGroupImpl getOrCreateEventListenerGroup(EventType 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 EventListenerGroupImpl getEventListenerGroup(EventType eventType) { - if ( registeredEventListeners.length < eventType.ordinal() + 1 ) { + public EventListenerGroup getEventListenerGroup(EventType 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 listeners = registeredEventListeners[ eventType.ordinal() ]; + final EventListenerGroup 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 instantiateListener(Class listenerClass) { try { + //noinspection deprecation return listenerClass.newInstance(); } catch ( Exception e ) { @@ -265,7 +164,7 @@ public class EventListenerRegistryImpl implements EventListenerRegistry, Stoppab @Override @SafeVarargs public final void setListeners(EventType type, T... listeners) { - EventListenerGroupImpl registeredListeners = getOrCreateEventListenerGroup( type ); + final EventListenerGroup 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 void appendListeners(EventType 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 void prependListeners(EventType 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 void prepareListeners(EventType type, EventListenerGroupImpl[] listenerArray) { - prepareListeners( type, null, listenerArray ); - } - - private void prepareListeners(EventType type, T defaultListener, EventListenerGroupImpl[] listenerArray) { - final EventListenerGroupImpl listenerGroup; - if ( type == EventType.POST_COMMIT_DELETE - || type == EventType.POST_COMMIT_INSERT - || type == EventType.POST_COMMIT_UPDATE ) { - listenerGroup = new PostCommitEventListenerGroupImpl<>( type, this ); - } - else { - listenerGroup = new EventListenerGroupImpl<>( 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,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 void prepareListeners(EventType eventType) { + prepareListeners( eventType, null ); + } + + public void prepareListeners(EventType 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 void prepareListeners( + EventType type, + T defaultListener, + Function,EventListenerGroupImpl> groupCreator) { + final EventListenerGroupImpl listenerGroup = groupCreator.apply( type ); + + if ( defaultListener != null ) { + listenerGroup.appendListener( defaultListener ); + } + + listenerGroupMap.put( type, listenerGroup ); + } + + public EventListenerGroup getListenerGroup(EventType eventType) { + //noinspection unchecked + return (EventListenerGroup) listenerGroupMap.get( eventType ); + } + + @SuppressWarnings("rawtypes") + public EventListenerRegistry buildRegistry(Map registeredEventTypes) { + // validate contiguity of the event-type ordinals and build the EventListenerGroups array + + final ArrayList 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 ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerServiceInitiator.java b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerServiceInitiator.java deleted file mode 100644 index 2cedca3eb5..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerServiceInitiator.java +++ /dev/null @@ -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 . - */ -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 { - public static final EventListenerServiceInitiator INSTANCE = new EventListenerServiceInitiator(); - - @Override - public Class 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()); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/event/service/internal/PostCommitEventListenerGroupImpl.java b/hibernate-core/src/main/java/org/hibernate/event/service/internal/PostCommitEventListenerGroupImpl.java index b7105a0ff5..9b20980291 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/service/internal/PostCommitEventListenerGroupImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/event/service/internal/PostCommitEventListenerGroupImpl.java @@ -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 extends EventListenerGroupImpl { private final Class extendedListenerContract; - public PostCommitEventListenerGroupImpl(EventType eventType, EventListenerRegistryImpl listenerRegistry) { - super( eventType, listenerRegistry ); + public PostCommitEventListenerGroupImpl( + EventType eventType, + CallbackRegistry callbackRegistry, + boolean isJpaBootstrap) { + super( eventType, callbackRegistry, isJpaBootstrap ); if ( eventType == EventType.POST_COMMIT_DELETE ) { this.extendedListenerContract = PostCommitDeleteEventListener.class; diff --git a/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventListenerRegistry.java b/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventListenerRegistry.java index 44afb85b60..5f99ab3186 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventListenerRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/event/service/spi/EventListenerRegistry.java @@ -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 + } EventListenerGroup getEventListenerGroup(EventType eventType); diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/EventEngine.java b/hibernate-core/src/main/java/org/hibernate/event/spi/EventEngine.java new file mode 100644 index 0000000000..dbf18b0995 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/EventEngine.java @@ -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 . + */ +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 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 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 eventTypes = new HashMap<>(); + EventType.registerStandardTypes( eventTypes ); + + final EventEngineContributions contributionManager = new EventEngineContributions() { + @Override + public EventType findEventType(String name) { + //noinspection unchecked + return eventTypes.get( name ); + } + + @Override + public EventType contributeEventType(String name, Class listenerRole) { + final EventType eventType = registerEventType( name, listenerRole ); + + listenerRegistryBuilder.prepareListeners( eventType ); + + return eventType; + } + + private EventType registerEventType(String name, Class 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 eventType = EventType.create( + name, + listenerRole, + eventTypes.size() + ); + + eventTypes.put( name, eventType ); + return eventType; + } + + @Override + public EventType contributeEventType(String name, Class listenerRole, T... defaultListeners) { + final EventType eventType = contributeEventType( name, listenerRole ); + + if ( defaultListeners != null ) { + final EventListenerGroup listenerGroup = listenerRegistryBuilder.getListenerGroup( eventType ); + listenerGroup.appendListeners( defaultListeners ); + } + + return eventType; + } + + @Override + public void configureListeners( + EventType eventType, + Consumer> action) { + if ( ! eventTypes.containsValue( eventType ) ) { + throw new HibernateException( "EventType [" + eventType + "] not registered" ); + } + + action.accept( listenerRegistryBuilder.getListenerGroup( eventType ) ); + } + }; + + final Collection 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> getRegisteredEventTypes() { + //noinspection unchecked,rawtypes + return (Collection) registeredEventTypes.values(); + } + + public EventType 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(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/EventEngineContributions.java b/hibernate-core/src/main/java/org/hibernate/event/spi/EventEngineContributions.java new file mode 100644 index 0000000000..8dbefe7383 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/EventEngineContributions.java @@ -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 . + */ +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 + */ + EventType 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 + */ + EventType contributeEventType(String name, Class listenerRole); + + /** + * Register a custom event type with a default listener. + */ + EventType contributeEventType(String name, Class listenerRole, T... defaultListener); + + /** + * Perform an action against the listener group for the specified event-type + */ + void configureListeners(EventType eventType, Consumer> action); +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/EventEngineContributor.java b/hibernate-core/src/main/java/org/hibernate/event/spi/EventEngineContributor.java new file mode 100644 index 0000000000..3b7246b9b1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/EventEngineContributor.java @@ -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 . + */ +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); +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/EventType.java b/hibernate-core/src/main/java/org/hibernate/event/spi/EventType.java index 4ce270e66b..9815756881 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/EventType.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/EventType.java @@ -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 { - 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 LOAD = create( "load", LoadEventListener.class ); public static final EventType RESOLVE_NATURAL_ID = create( "resolve-natural-id", ResolveNaturalIdEventListener.class ); @@ -128,11 +128,12 @@ public final class EventType { * Maintain a map of {@link EventType} instances keyed by name for lookup by name as well as {@link #values()} * resolution. */ - private static final Map EVENT_TYPE_BY_NAME_MAP = AccessController.doPrivileged( + @SuppressWarnings({"rawtypes", "Convert2Lambda"}) + private static final Map STANDARD_TYPE_BY_NAME_MAP = AccessController.doPrivileged( new PrivilegedAction>() { @Override public Map run() { - final Map typeByNameMap = new ConcurrentHashMap<>(); + final Map typeByNameMap = new HashMap<>(); for ( Field field : EventType.class.getDeclaredFields() ) { if ( EventType.class.isAssignableFrom( field.getType() ) ) { try { @@ -144,11 +145,20 @@ public final class EventType { } } } - return typeByNameMap; + + return Collections.unmodifiableMap( typeByNameMap ); } } ); + private static EventType create(String name, Class listenerRole) { + return new EventType<>( name, listenerRole, STANDARD_TYPE_COUNTER.getAndIncrement(), true ); + } + + public static EventType create(String name, Class listenerRole, int ordinal) { + return new EventType<>( name, listenerRole, ordinal, false ); + } + /** * Find an {@link EventType} by its name * @@ -158,11 +168,12 @@ public final class EventType { * * @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 +181,44 @@ public final class EventType { } /** - * 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 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 eventTypes) { + eventTypes.putAll( STANDARD_TYPE_BY_NAME_MAP ); } private final String eventName; private final Class baseListenerInterface; private final int ordinal; + private final boolean isStandardEvent; - private EventType(String eventName, Class baseListenerInterface) { + private EventType(String eventName, Class 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 +231,15 @@ public final class EventType { return ordinal; } + /** + * Is this event-type one of the standard event-types? + */ + public boolean isStandardEvent() { + return isStandardEvent; + } + + @Override + public String toString() { + return eventName(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index 8cb4e9388c..21edee35a8 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -82,6 +82,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; @@ -171,6 +172,7 @@ public class SessionFactoryImpl implements SessionFactoryImplementor { private final transient Map properties; private final transient SessionFactoryServiceRegistry serviceRegistry; + private final transient EventEngine eventEngine; private final transient JdbcServices jdbcServices; // todo : org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor too? @@ -212,6 +214,8 @@ public class SessionFactoryImpl implements SessionFactoryImplementor { bootMetamodel.initSessionFactory( this ); + this.eventEngine = new EventEngine( metadata, this ); + final CfgXmlAccessService cfgXmlAccessService = serviceRegistry.getService( CfgXmlAccessService.class ); String sfName = settings.getSessionFactoryName(); @@ -578,6 +582,11 @@ public class SessionFactoryImpl implements SessionFactoryImplementor { return queryEngine; } + @Override + public EventEngine getEventEngine() { + return eventEngine; + } + @Override public JdbcServices getJdbcServices() { return jdbcServices; diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/event/internal/CallbacksFactory.java b/hibernate-core/src/main/java/org/hibernate/jpa/event/internal/CallbacksFactory.java index 3365c5a44e..0561b8edbc 100644 --- a/hibernate-core/src/main/java/org/hibernate/jpa/event/internal/CallbacksFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/jpa/event/internal/CallbacksFactory.java @@ -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(); } diff --git a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/AbstractLogicalConnectionImplementor.java b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/AbstractLogicalConnectionImplementor.java index 0202fa637d..8b6857002e 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/AbstractLogicalConnectionImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/AbstractLogicalConnectionImplementor.java @@ -48,6 +48,11 @@ public abstract class AbstractLogicalConnectionImplementor implements LogicalCon log.trace( "LogicalConnection#afterStatement" ); } + @Override + public void beforeTransactionCompletion() { + log.trace( "LogicalConnection#beforeTransactionCompletion" ); + } + @Override public void afterTransaction() { log.trace( "LogicalConnection#afterTransaction" ); diff --git a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java index af0409fc14..e6918738ed 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java @@ -148,6 +148,15 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple } } + @Override + public void beforeTransactionCompletion() { + super.beforeTransactionCompletion(); + if ( connectionHandlingMode.getReleaseMode() == ConnectionReleaseMode.BEFORE_TRANSACTION_COMPLETION ) { + log.debug( "Initiating JDBC connection release from beforeTransactionCompletion" ); + releaseConnection(); + } + } + @Override public void afterTransaction() { super.afterTransaction(); diff --git a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/LogicalConnectionImplementor.java b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/LogicalConnectionImplementor.java index 8f656183e9..39274044ba 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/LogicalConnectionImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/LogicalConnectionImplementor.java @@ -35,6 +35,13 @@ public interface LogicalConnectionImplementor extends LogicalConnection { */ void afterStatement(); + /** + * Notification indicating a transaction is about to be completed, so to trigger + * releasing of the connection if needed ({@link org.hibernate.ConnectionReleaseMode#BEFORE_TRANSACTION_COMPLETION} + * is enabled) + */ + void beforeTransactionCompletion(); + /** * Notification indicating a transaction has completed to trigger * {@link org.hibernate.ConnectionReleaseMode#AFTER_TRANSACTION} releasing if needed diff --git a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/PhysicalConnectionHandlingMode.java b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/PhysicalConnectionHandlingMode.java index 5d60cf6336..37bfb3336d 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/PhysicalConnectionHandlingMode.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/spi/PhysicalConnectionHandlingMode.java @@ -15,6 +15,7 @@ import static org.hibernate.ConnectionAcquisitionMode.AS_NEEDED; import static org.hibernate.ConnectionAcquisitionMode.IMMEDIATELY; import static org.hibernate.ConnectionReleaseMode.AFTER_STATEMENT; import static org.hibernate.ConnectionReleaseMode.AFTER_TRANSACTION; +import static org.hibernate.ConnectionReleaseMode.BEFORE_TRANSACTION_COMPLETION; import static org.hibernate.ConnectionReleaseMode.ON_CLOSE; /** @@ -39,6 +40,11 @@ public enum PhysicalConnectionHandlingMode { * after each statement is executed. */ DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT( AS_NEEDED, AFTER_STATEMENT ), + /** + * The Connection will be acquired as soon as it is needed; it will be released + * before commit/rollback. + */ + DELAYED_ACQUISITION_AND_RELEASE_BEFORE_TRANSACTION_COMPLETION( AS_NEEDED, BEFORE_TRANSACTION_COMPLETION ), /** * The Connection will be acquired as soon as it is needed; it will be released * after each transaction is completed. @@ -99,6 +105,9 @@ public enum PhysicalConnectionHandlingMode { case AFTER_STATEMENT: { return DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT; } + case BEFORE_TRANSACTION_COMPLETION: { + return DELAYED_ACQUISITION_AND_RELEASE_BEFORE_TRANSACTION_COMPLETION; + } case AFTER_TRANSACTION: { return DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION; } diff --git a/hibernate-core/src/main/java/org/hibernate/service/internal/SessionFactoryServiceRegistryImpl.java b/hibernate-core/src/main/java/org/hibernate/service/internal/SessionFactoryServiceRegistryImpl.java index d6086864fb..70cfc9a06a 100644 --- a/hibernate-core/src/main/java/org/hibernate/service/internal/SessionFactoryServiceRegistryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/service/internal/SessionFactoryServiceRegistryImpl.java @@ -21,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 */ @@ -28,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 initiators, @@ -63,7 +66,7 @@ public class SessionFactoryServiceRegistryImpl @Override public void configureService(ServiceBinding serviceBinding) { - if ( Configurable.class.isInstance( serviceBinding.getService() ) ) { + if ( serviceBinding.getService() instanceof Configurable ) { ( (Configurable) serviceBinding.getService() ).configure( getService( ConfigurationService.class ).getSettings() ); } } @@ -85,22 +88,16 @@ public class SessionFactoryServiceRegistryImpl @Override public R getService(Class 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; - } - } diff --git a/hibernate-core/src/main/java/org/hibernate/service/internal/StandardSessionFactoryServiceInitiators.java b/hibernate-core/src/main/java/org/hibernate/service/internal/StandardSessionFactoryServiceInitiators.java index 470d087f04..a76413841e 100644 --- a/hibernate-core/src/main/java/org/hibernate/service/internal/StandardSessionFactoryServiceInitiators.java +++ b/hibernate-core/src/main/java/org/hibernate/service/internal/StandardSessionFactoryServiceInitiators.java @@ -11,7 +11,6 @@ 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; @@ -26,7 +25,6 @@ public final class StandardSessionFactoryServiceInitiators { public static List buildStandardServiceInitiatorList() { final ArrayList serviceInitiators = new ArrayList<>(); - serviceInitiators.add( EventListenerServiceInitiator.INSTANCE ); serviceInitiators.add( StatisticsInitiator.INSTANCE ); serviceInitiators.add( CacheInitiator.INSTANCE ); serviceInitiators.add( NativeQueryInterpreterInitiator.INSTANCE ); diff --git a/hibernate-core/src/test/java/org/hibernate/event/CustomEventTypeTest.java b/hibernate-core/src/test/java/org/hibernate/event/CustomEventTypeTest.java deleted file mode 100644 index 5bbbc8dbe8..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/event/CustomEventTypeTest.java +++ /dev/null @@ -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 . - */ -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 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 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 { - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/event/service/internal/EventListenerDuplicationStrategyTest.java b/hibernate-core/src/test/java/org/hibernate/event/service/internal/EventListenerDuplicationStrategyTest.java index 0f51bea9a0..e87364228f 100644 --- a/hibernate-core/src/test/java/org/hibernate/event/service/internal/EventListenerDuplicationStrategyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/event/service/internal/EventListenerDuplicationStrategyTest.java @@ -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 listenerGroup = new EventListenerGroupImpl( EventType.CLEAR, null ); + EventListenerGroup listenerGroup = new EventListenerGroupImpl( EventType.CLEAR, null, false ); @Test public void testListenersIterator() { diff --git a/hibernate-core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorNoIncrementTest.java b/hibernate-core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorNoIncrementTest.java index 83340c7547..433cef1cb0 100644 --- a/hibernate-core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorNoIncrementTest.java +++ b/hibernate-core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorNoIncrementTest.java @@ -8,7 +8,6 @@ package org.hibernate.id; import java.util.Properties; -import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.boot.Metadata; import org.hibernate.boot.MetadataSources; @@ -47,7 +46,7 @@ public class SequenceHiLoGeneratorNoIncrementTest extends BaseUnitTestCase { private StandardServiceRegistry serviceRegistry; private SessionFactoryImplementor sessionFactory; - private SequenceStyleGenerator generator; + private SequenceGenerator generator; private SessionImplementor sessionImpl; private SequenceValueExtractor sequenceValueExtractor; @@ -58,22 +57,19 @@ public class SequenceHiLoGeneratorNoIncrementTest extends BaseUnitTestCase { .applySetting( AvailableSettings.HBM2DDL_AUTO, "create-drop" ) .build(); - generator = new SequenceStyleGenerator(); + MetadataBuildingContext buildingContext = new MetadataBuildingContextTestingImpl( serviceRegistry ); // Build the properties used to configure the id generator Properties properties = new Properties(); - properties.setProperty( SequenceStyleGenerator.SEQUENCE_PARAM, TEST_SEQUENCE ); - properties.setProperty( SequenceStyleGenerator.OPT_PARAM, "legacy-hilo" ); - properties.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "0" ); // JPA allocationSize of 1 + properties.setProperty( SequenceGenerator.SEQUENCE, TEST_SEQUENCE ); + properties.setProperty( SequenceHiLoGenerator.MAX_LO, "0" ); // JPA allocationSize of 1 + properties.put( PersistentIdentifierGenerator.IDENTIFIER_NORMALIZER, - new ObjectNameNormalizer() { - @Override - protected MetadataBuildingContext getBuildingContext() { - return new MetadataBuildingContextTestingImpl( serviceRegistry ); - } - } + buildingContext.getObjectNameNormalizer() ); + + generator = new SequenceHiLoGenerator(); generator.configure( StandardBasicTypes.LONG, properties, serviceRegistry ); final Metadata metadata = new MetadataSources( serviceRegistry ).buildMetadata(); @@ -84,9 +80,9 @@ public class SequenceHiLoGeneratorNoIncrementTest extends BaseUnitTestCase { } @After - public void tearDown() throws Exception { + public void tearDown() { if ( sessionImpl != null && !sessionImpl.isClosed() ) { - ((Session) sessionImpl).close(); + sessionImpl.close(); } if ( sessionFactory != null ) { sessionFactory.close(); @@ -100,31 +96,23 @@ public class SequenceHiLoGeneratorNoIncrementTest extends BaseUnitTestCase { public void testHiLoAlgorithm() { sessionImpl = (SessionImpl) sessionFactory.openSession(); - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // historically the hilo generators skipped the initial block of values; - // so the first generated id value is maxlo + 1, here be 4 assertEquals( 1L, generateValue() ); - // which should also perform the first read on the sequence which should set it to its "start with" value (1) assertEquals( 1L, extractSequenceValue() ); - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ assertEquals( 2L, generateValue() ); assertEquals( 2L, extractSequenceValue() ); - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ assertEquals( 3L, generateValue() ); assertEquals( 3L, extractSequenceValue() ); - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ assertEquals( 4L, generateValue() ); assertEquals( 4L, extractSequenceValue() ); - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ assertEquals( 5L, generateValue() ); assertEquals( 5L, extractSequenceValue() ); - ((Session) sessionImpl).close(); + sessionImpl.close(); } private long extractSequenceValue() { @@ -133,7 +121,7 @@ public class SequenceHiLoGeneratorNoIncrementTest extends BaseUnitTestCase { private long generateValue() { Long generatedValue; - Transaction transaction = ((Session) sessionImpl).beginTransaction(); + Transaction transaction = sessionImpl.beginTransaction(); generatedValue = (Long) generator.generate( sessionImpl, null ); transaction.commit(); return generatedValue.longValue(); diff --git a/hibernate-core/src/test/java/org/hibernate/id/SequenceStyleGeneratorBehavesLikeSequeceHiloGeneratorWitZeroIncrementSizeTest.java b/hibernate-core/src/test/java/org/hibernate/id/SequenceStyleGeneratorBehavesLikeSequeceHiloGeneratorWitZeroIncrementSizeTest.java new file mode 100644 index 0000000000..a818fa483a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/id/SequenceStyleGeneratorBehavesLikeSequeceHiloGeneratorWitZeroIncrementSizeTest.java @@ -0,0 +1,127 @@ +/* + * 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.id; + +import java.util.Properties; + +import org.hibernate.Transaction; +import org.hibernate.boot.Metadata; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.model.naming.ObjectNameNormalizer; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.id.enhanced.SequenceStyleGenerator; +import org.hibernate.internal.SessionImpl; +import org.hibernate.type.StandardBasicTypes; + +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; +import org.hibernate.testing.boot.MetadataBuildingContextTestingImpl; +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Andrea Boriero + */ +@RequiresDialectFeature(DialectChecks.SupportsSequences.class) +public class SequenceStyleGeneratorBehavesLikeSequeceHiloGeneratorWitZeroIncrementSizeTest extends BaseUnitTestCase { + private static final String TEST_SEQUENCE = "test_sequence"; + + private StandardServiceRegistry serviceRegistry; + private SessionFactoryImplementor sessionFactory; + private SequenceStyleGenerator generator; + private SessionImplementor sessionImpl; + private SequenceValueExtractor sequenceValueExtractor; + + @Before + public void setUp() throws Exception { + serviceRegistry = new StandardServiceRegistryBuilder() + .enableAutoClose() + .applySetting( AvailableSettings.HBM2DDL_AUTO, "create-drop" ) + .build(); + + generator = new SequenceStyleGenerator(); + + // Build the properties used to configure the id generator + Properties properties = new Properties(); + properties.setProperty( SequenceStyleGenerator.SEQUENCE_PARAM, TEST_SEQUENCE ); + properties.setProperty( SequenceStyleGenerator.OPT_PARAM, "legacy-hilo" ); + properties.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "0" ); // JPA allocationSize of 1 + properties.put( + PersistentIdentifierGenerator.IDENTIFIER_NORMALIZER, + new ObjectNameNormalizer() { + @Override + protected MetadataBuildingContext getBuildingContext() { + return new MetadataBuildingContextTestingImpl( serviceRegistry ); + } + } + ); + generator.configure( StandardBasicTypes.LONG, properties, serviceRegistry ); + + final Metadata metadata = new MetadataSources( serviceRegistry ).buildMetadata(); + generator.registerExportables( metadata.getDatabase() ); + + sessionFactory = (SessionFactoryImplementor) metadata.buildSessionFactory(); + sequenceValueExtractor = new SequenceValueExtractor( sessionFactory.getDialect(), TEST_SEQUENCE ); + } + + @After + public void tearDown() { + if ( sessionImpl != null && !sessionImpl.isClosed() ) { + sessionImpl.close(); + } + if ( sessionFactory != null ) { + sessionFactory.close(); + } + if ( serviceRegistry != null ) { + StandardServiceRegistryBuilder.destroy( serviceRegistry ); + } + } + + @Test + public void testHiLoAlgorithm() { + sessionImpl = (SessionImpl) sessionFactory.openSession(); + + assertEquals( 1L, generateValue() ); + + assertEquals( 1L, extractSequenceValue() ); + + assertEquals( 2L, generateValue() ); + assertEquals( 2L, extractSequenceValue() ); + + assertEquals( 3L, generateValue() ); + assertEquals( 3L, extractSequenceValue() ); + + assertEquals( 4L, generateValue() ); + assertEquals( 4L, extractSequenceValue() ); + + assertEquals( 5L, generateValue() ); + assertEquals( 5L, extractSequenceValue() ); + + sessionImpl.close(); + } + + private long extractSequenceValue() { + return sequenceValueExtractor.extractSequenceValue( sessionImpl ); + } + + private long generateValue() { + Long generatedValue; + Transaction transaction = sessionImpl.beginTransaction(); + generatedValue = (Long) generator.generate( sessionImpl, null ); + transaction.commit(); + return generatedValue.longValue(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/connections/BeforeCompletionReleaseTest.java b/hibernate-core/src/test/java/org/hibernate/test/connections/BeforeCompletionReleaseTest.java new file mode 100644 index 0000000000..f932b558fe --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/connections/BeforeCompletionReleaseTest.java @@ -0,0 +1,161 @@ +/* + * 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.test.connections; + +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; +import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; +import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.env.ConnectionProviderBuilder; +import org.hibernate.testing.jta.TestingJtaBootstrap; +import org.hibernate.testing.jta.TestingJtaPlatformImpl; +import org.hibernate.testing.transaction.TransactionUtil; +import org.junit.Test; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.transaction.RollbackException; +import javax.transaction.SystemException; +import javax.transaction.Transaction; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author Luis Barreiro + */ +@RequiresDialect( H2Dialect.class ) +public class BeforeCompletionReleaseTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Map getConfig() { + Map config = super.getConfig(); + TestingJtaBootstrap.prepare( config ); + config.put( AvailableSettings.CONNECTION_PROVIDER, new ConnectionProviderDecorator() ); + config.put( AvailableSettings.CONNECTION_HANDLING, PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_BEFORE_TRANSACTION_COMPLETION ); + return config; + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Thing.class }; + } + + @Test + public void testConnectionAcquisitionCount() { + TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> { + Thing thing = new Thing(); + thing.setId( 1 ); + entityManager.persist( thing ); + }); + } + + // --- // + + @Entity(name = "Thing") + @Table(name = "Thing") + public static class Thing { + + @Id + public Integer id; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + } + + // --- // + + public static class ConnectionProviderDecorator extends UserSuppliedConnectionProviderImpl { + + private final ConnectionProvider dataSource; + + public ConnectionProviderDecorator() { + this.dataSource = ConnectionProviderBuilder.buildConnectionProvider(); + } + + @Override + public Connection getConnection() throws SQLException { + Connection connection = dataSource.getConnection(); + + try { + Transaction tx = TestingJtaPlatformImpl.transactionManager().getTransaction(); + if ( tx != null) { + tx.enlistResource( new XAResource() { + + @Override public void commit(Xid xid, boolean onePhase) { + try { + assertTrue( "Connection should be closed prior to commit", connection.isClosed() ); + } catch ( SQLException e ) { + fail( "Unexpected SQLException: " + e.getMessage() ); + } + } + + @Override public void end(Xid xid, int flags) { + } + + @Override public void forget(Xid xid) { + } + + @Override public int getTransactionTimeout() { + return 0; + } + + @Override public boolean isSameRM(XAResource xares) { + return false; + } + + @Override public int prepare(Xid xid) { + return 0; + } + + @Override public Xid[] recover(int flag) { + return new Xid[0]; + } + + @Override public void rollback(Xid xid) { + try { + assertTrue( "Connection should be closed prior to rollback", connection.isClosed() ); + } catch ( SQLException e ) { + fail( "Unexpected SQLException: " + e.getMessage() ); + } + } + + @Override public boolean setTransactionTimeout(int seconds) { + return false; + } + + @Override public void start(Xid xid, int flags) { + } + }); + } + } catch ( SystemException | RollbackException e ) { + fail( e.getMessage() ); + } + return connection; + } + + @Override + public void closeConnection(Connection connection) throws SQLException { + connection.close(); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/events/CustomEventTypeRegisterListenerTest.java b/hibernate-core/src/test/java/org/hibernate/test/events/CustomEventTypeRegisterListenerTest.java deleted file mode 100644 index 0480cfe7aa..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/events/CustomEventTypeRegisterListenerTest.java +++ /dev/null @@ -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 . - */ -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 void testNormalUsage(EventType eventType, Class 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 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 eventTypeForSetListenerClasses; - private EventType eventTypeForSetListenerObjects; - - private EventType eventTypeForPrependListenerClasses; - private EventType eventTypeForPrependListenerObjects; - - private EventType eventTypeForAppendListenerClasses; - private EventType eventTypeForAppendListenerObjects; - - private EventType 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 eventTypeForSetListenerClasses() { - return eventTypeForSetListenerClasses; - } - - public EventType eventTypeForSetListenerObjects() { - return eventTypeForSetListenerObjects; - } - - public EventType eventTypeForPrependListenerClasses() { - return eventTypeForPrependListenerClasses; - } - - public EventType eventTypeForPrependListenerObjects() { - return eventTypeForPrependListenerObjects; - } - - public EventType eventTypeForAppendListenerClasses() { - return eventTypeForAppendListenerClasses; - } - - public EventType eventTypeForAppendListenerObjects() { - return eventTypeForAppendListenerObjects; - } - - public EventType eventTypeUnregistered() { - return eventTypeUnregistered; - } - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/test/events/EventEngineContributionsTests.java b/hibernate-core/src/test/java/org/hibernate/test/events/EventEngineContributionsTests.java new file mode 100644 index 0000000000..b0fa04a569 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/events/EventEngineContributionsTests.java @@ -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 . + */ +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 saveEventType = eventEngine.findRegisteredEventType( SexyRxySaveListener.EVENT_NAME ); + assertThat( saveEventType, sameInstance( TheContributor.INSTANCE.saveEventType ) ); + assertThat( saveEventType.isStandardEvent(), is( false ) ); + + final EventListenerRegistry listenerRegistry = eventEngine.getListenerRegistry(); + final EventListenerGroup listenerGroup = listenerRegistry.getEventListenerGroup( saveEventType ); + assertThat( listenerGroup.count(), is( 1 ) ); + + listenerGroup.fireEventOnEachListener( RxySaveEvent.INSTANCE, SexyRxySaveListener::doIt ); + + assertThat( SexyRxySaveListener.INSTANCE.didIt, is(true ) ); + } + + { + final EventType persistEventType = eventEngine.findRegisteredEventType( SexyRxyPersistListener.EVENT_NAME ); + assertThat( persistEventType, sameInstance( TheContributor.INSTANCE.persistEventType ) ); + assertThat( persistEventType.isStandardEvent(), is( false ) ); + + final EventListenerRegistry listenerRegistry = eventEngine.getListenerRegistry(); + final EventListenerGroup 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 saveEventType; + private EventType 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 Collection loadJavaServices(Class serviceContract) { + if ( serviceContract.equals( EventEngineContributor.class ) ) { + //noinspection unchecked + return (Collection) Collections.singleton( TheContributor.INSTANCE ); + } + + return super.loadJavaServices( serviceContract ); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/events/EventTypeListenerRegistryConcurrencyTest.java b/hibernate-core/src/test/java/org/hibernate/test/events/EventTypeListenerRegistryConcurrencyTest.java deleted file mode 100644 index 7e1a727437..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/events/EventTypeListenerRegistryConcurrencyTest.java +++ /dev/null @@ -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 . - */ -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 exceptions = new ArrayList<>(); - private final Set 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 { - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/test/strategyselectors/DefaultDialectSelectorTest.java b/hibernate-core/src/test/java/org/hibernate/test/strategyselectors/DefaultDialectSelectorTest.java new file mode 100644 index 0000000000..96c1f0b6ec --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/strategyselectors/DefaultDialectSelectorTest.java @@ -0,0 +1,84 @@ +/* + * 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.test.strategyselectors; + +import org.hibernate.boot.registry.selector.internal.DefaultDialectSelector; +import org.hibernate.dialect.*; + +import org.junit.Assert; +import org.junit.Test; + +public class DefaultDialectSelectorTest { + + private DefaultDialectSelector strategySelector = new DefaultDialectSelector(); + + @Test + public void verifyAllDialectNamingResolve() { + testDialectNamingResolution( Cache71Dialect.class ); + testDialectNamingResolution( CUBRIDDialect.class ); + testDialectNamingResolution( DB2Dialect.class ); + testDialectNamingResolution( DB2390Dialect.class ); + testDialectNamingResolution( DB2390V8Dialect.class ); + testDialectNamingResolution( DB2400Dialect.class ); + testDialectNamingResolution( DB2400V7R3Dialect.class ); + testDialectNamingResolution( DerbyTenFiveDialect.class ); + testDialectNamingResolution( DerbyTenSixDialect.class ); + testDialectNamingResolution( DerbyTenSevenDialect.class ); + testDialectNamingResolution( FirebirdDialect.class ); + testDialectNamingResolution( FrontBaseDialect.class ); + testDialectNamingResolution( H2Dialect.class ); + testDialectNamingResolution( HANAColumnStoreDialect.class ); + testDialectNamingResolution( HANARowStoreDialect.class ); + testDialectNamingResolution( HSQLDialect.class ); + testDialectNamingResolution( InformixDialect.class ); + testDialectNamingResolution( IngresDialect.class ); + testDialectNamingResolution( Ingres9Dialect.class ); + testDialectNamingResolution( Ingres10Dialect.class ); + testDialectNamingResolution( InterbaseDialect.class ); + testDialectNamingResolution( JDataStoreDialect.class ); + + testDialectNamingResolution( MckoiDialect.class ); + testDialectNamingResolution( MimerSQLDialect.class ); + testDialectNamingResolution( MySQL5Dialect.class ); + testDialectNamingResolution( MySQL5InnoDBDialect.class ); + testDialectNamingResolution( MySQL57InnoDBDialect.class ); + testDialectNamingResolution( MySQL57Dialect.class ); + testDialectNamingResolution( MySQL8Dialect.class ); + testDialectNamingResolution( Oracle8iDialect.class ); + testDialectNamingResolution( Oracle9iDialect.class ); + testDialectNamingResolution( Oracle10gDialect.class ); + + testDialectNamingResolution( PointbaseDialect.class ); + testDialectNamingResolution( PostgresPlusDialect.class ); + testDialectNamingResolution( PostgreSQL81Dialect.class ); + testDialectNamingResolution( PostgreSQL82Dialect.class ); + testDialectNamingResolution( PostgreSQL9Dialect.class ); + testDialectNamingResolution( ProgressDialect.class ); + + testDialectNamingResolution( SAPDBDialect.class ); + testDialectNamingResolution( SQLServerDialect.class ); + testDialectNamingResolution( SQLServer2005Dialect.class ); + testDialectNamingResolution( SQLServer2008Dialect.class ); + testDialectNamingResolution( Sybase11Dialect.class ); + testDialectNamingResolution( SybaseAnywhereDialect.class ); + testDialectNamingResolution( SybaseASE15Dialect.class ); + testDialectNamingResolution( SybaseASE157Dialect.class ); + testDialectNamingResolution( TeradataDialect.class ); + testDialectNamingResolution( TimesTenDialect.class ); + } + + private void testDialectNamingResolution(final Class dialectClass) { + String simpleName = dialectClass.getSimpleName(); + if ( simpleName.endsWith( "Dialect" ) ) { + simpleName = simpleName.substring( 0, simpleName.length() - "Dialect".length() ); + } + Class aClass = strategySelector.resolve( simpleName ); + Assert.assertNotNull( aClass ); + Assert.assertEquals( dialectClass, aClass ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/strategyselectors/JtaPlatformSelectorTest.java b/hibernate-core/src/test/java/org/hibernate/test/strategyselectors/JtaPlatformSelectorTest.java new file mode 100644 index 0000000000..1ce871e3f1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/strategyselectors/JtaPlatformSelectorTest.java @@ -0,0 +1,174 @@ +/* + * 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.test.strategyselectors; + +import org.hibernate.boot.registry.selector.internal.DefaultJtaPlatformSelector; +import org.hibernate.engine.transaction.jta.platform.internal.AtomikosJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.BitronixJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.BorlandEnterpriseServerJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.JBossAppServerJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.JBossStandAloneJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.JOTMJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.JOnASJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.JRun4JtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.OC4JJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.OrionJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.ResinJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.SapNetWeaverJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.SunOneJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.WebSphereExtendedJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.WebSphereJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.WebSphereLibertyJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.internal.WeblogicJtaPlatform; +import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; + +import org.junit.Assert; +import org.junit.Test; + +public class JtaPlatformSelectorTest { + + private final DefaultJtaPlatformSelector strategySelector = new DefaultJtaPlatformSelector(); + + @Test + public void verifyAllJtaPlatformResolve() { + + // N.B. it might seem that there is some reduncancy, but it's not the case: the FQNs listed + // here are legacy full classnames which are being re-mapped to the right class for backwards + // compatibility reasons. + + testJtaPlatformResolves( + strategySelector, + AtomikosJtaPlatform.class, + "Atomikos", + "org.hibernate.service.jta.platform.internal.AtomikosJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + BorlandEnterpriseServerJtaPlatform.class, + "Borland", + "org.hibernate.service.jta.platform.internal.BorlandEnterpriseServerJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + BitronixJtaPlatform.class, + "Bitronix", + "org.hibernate.service.jta.platform.internal.BitronixJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + JBossAppServerJtaPlatform.class, + "JBossAS", + "org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + JBossStandAloneJtaPlatform.class, + "JBossTS", + "org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + JOnASJtaPlatform.class, + "JOnAS", + "org.hibernate.service.jta.platform.internal.JOnASJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + JOTMJtaPlatform.class, + "JOTM", + "org.hibernate.service.jta.platform.internal.JOTMJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + JRun4JtaPlatform.class, + "JRun4", + "org.hibernate.service.jta.platform.internal.JRun4JtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + OC4JJtaPlatform.class, + "OC4J", + "org.hibernate.service.jta.platform.internal.OC4JJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + OrionJtaPlatform.class, + "Orion", + "org.hibernate.service.jta.platform.internal.OrionJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + ResinJtaPlatform.class, + "Resin", + "org.hibernate.service.jta.platform.internal.ResinJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + SapNetWeaverJtaPlatform.class, + "SapNetWeaver", + "org.hibernate.service.jta.platform.internal.SapNetWeaverJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + SunOneJtaPlatform.class, + "SunOne", + "org.hibernate.service.jta.platform.internal.SunOneJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + WeblogicJtaPlatform.class, + "Weblogic", + "org.hibernate.service.jta.platform.internal.WeblogicJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + WebSphereLibertyJtaPlatform.class, + "WebSphereLiberty", + "org.hibernate.engine.transaction.jta.platform.internal.WebSphereLibertyJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + WebSphereJtaPlatform.class, + "WebSphere", + "org.hibernate.service.jta.platform.internal.WebSphereJtaPlatform" + ); + + testJtaPlatformResolves( + strategySelector, + WebSphereExtendedJtaPlatform.class, + "WebSphereExtended", + "org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform" + ); + } + + private static void testJtaPlatformResolves(final DefaultJtaPlatformSelector strategySelector, final Class expectedType, final String shortname, final String longname) { + expectResolution(strategySelector, expectedType, shortname); + expectResolution(strategySelector, expectedType, longname); + } + + private static void expectResolution(final DefaultJtaPlatformSelector strategySelector, final Class expectedType, final String name) { + Class aClass = strategySelector.resolve( name ); + Assert.assertNotNull( aClass ); + Assert.assertEquals( expectedType, aClass ); + } + +}