From a742f5e98882190959582d5a44d132c9973cd19b Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 25 Jan 2023 16:26:44 -0600 Subject: [PATCH] HHH-16069 - Skip CDI for Hibernate extensions by default --- .../boot/internal/MetadataBuilderImpl.java | 12 ++ .../hibernate/boot/model/TypeDefinition.java | 9 +- .../boot/model/internal/AnnotationBinder.java | 106 +++++++++++--- .../boot/model/internal/BasicValueBinder.java | 132 ++++++++++++------ .../boot/model/internal/CollectionBinder.java | 97 ++++++------- .../DelayedParameterizedTypeBean.java | 74 ++++++++++ .../boot/model/internal/EmbeddableBinder.java | 57 ++++---- .../source/internal/hbm/ModelBinder.java | 39 ++++-- ...ractDelegatingMetadataBuildingOptions.java | 8 +- .../boot/spi/MetadataBuildingOptions.java | 9 +- .../org/hibernate/cfg/AvailableSettings.java | 10 ++ .../StandardIdentifierGeneratorFactory.java | 2 +- .../org/hibernate/mapping/BasicValue.java | 28 ++-- .../org/hibernate/mapping/MappingHelper.java | 91 ++++++++++-- ...gedTypeRepresentationResolverStandard.java | 48 ++++--- .../resource/beans/internal/Helper.java | 21 +-- .../org/hibernate/type/ComponentType.java | 13 +- .../hibernate/type/spi/TypeConfiguration.java | 15 +- .../orm/test/cdi/type/CdiSmokeTests.java | 56 ++++++++ .../cdi/type/ExtendedBeanManagerImpl.java | 27 ++++ .../orm/test/cdi/type/OtherBean.java | 13 ++ .../orm/test/cdi/type/SimpleTests.java | 120 ++++++++++++++++ .../hibernate/orm/test/cdi/type/UrlType.java | 91 ++++++++++++ .../UserDefinedGeneratorsTests.java | 32 ++--- .../metadata/ComponentMetadataGenerator.java | 33 +++-- .../DefaultRevisionInfoGenerator.java | 8 ++ 26 files changed, 901 insertions(+), 250 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/model/internal/DelayedParameterizedTypeBean.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/CdiSmokeTests.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/ExtendedBeanManagerImpl.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/OtherBean.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/SimpleTests.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/UrlType.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java index f5303a9b38..ced669ef1d 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java @@ -607,6 +607,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont private final String schemaCharset; private final boolean xmlMappingEnabled; + private final boolean allowExtensionsInCdi; public MetadataBuildingOptionsImpl(StandardServiceRegistry serviceRegistry) { this.serviceRegistry = serviceRegistry; @@ -763,6 +764,12 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont String.class, null ); + + allowExtensionsInCdi = configService.getSetting( + AvailableSettings.ALLOW_EXTENSIONS_IN_CDI, + StandardConverters.BOOLEAN, + false + ); } private ArrayList resolveInitialSourceProcessOrdering(ConfigurationService configService) { @@ -951,6 +958,11 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont return xmlMappingEnabled; } + @Override + public boolean disallowExtensionsInCdi() { + return allowExtensionsInCdi; + } + /** * Yuck. This is needed because JPA lets users define "global building options" * in {@code orm.xml} mappings. Forget that there are generally multiple diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/TypeDefinition.java b/hibernate-core/src/main/java/org/hibernate/boot/model/TypeDefinition.java index 6a57c36377..8f4382b66e 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/TypeDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/TypeDefinition.java @@ -23,7 +23,6 @@ import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.mapping.BasicValue; import org.hibernate.metamodel.mapping.JdbcMapping; -import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; import org.hibernate.resource.beans.internal.Helper; import org.hibernate.resource.beans.spi.BeanInstanceProducer; import org.hibernate.resource.beans.spi.ManagedBean; @@ -32,6 +31,7 @@ import org.hibernate.type.BasicType; import org.hibernate.type.CustomType; import org.hibernate.type.SerializableType; import org.hibernate.type.Type; +import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.MutabilityPlan; @@ -137,6 +137,9 @@ public class TypeDefinition implements Serializable { Map usageSiteProperties, JdbcTypeIndicators indicators, MetadataBuildingContext context) { + if ( context.getBuildingOptions().disallowExtensionsInCdi() ) { + + } final BootstrapContext bootstrapContext = context.getBootstrapContext(); final TypeConfiguration typeConfiguration = bootstrapContext.getTypeConfiguration(); final BeanInstanceProducer instanceProducer = bootstrapContext.getCustomTypeProducer(); @@ -282,13 +285,13 @@ public class TypeDefinition implements Serializable { private static Object instantiateType(StandardServiceRegistry serviceRegistry, String name, Class typeImplementorClass, BeanInstanceProducer instanceProducer) { - if ( Helper.shouldIgnoreBeanContainer( serviceRegistry ) ) { + if ( !Helper.allowExtensionsInCdi( serviceRegistry ) ) { return name != null ? instanceProducer.produceBeanInstance( name, typeImplementorClass ) : instanceProducer.produceBeanInstance( typeImplementorClass ); } else { - ManagedBeanRegistry beanRegistry = serviceRegistry.getService(ManagedBeanRegistry.class); + final ManagedBeanRegistry beanRegistry = serviceRegistry.getService( ManagedBeanRegistry.class ); final ManagedBean typeBean = name != null ? beanRegistry.getBean( name, typeImplementorClass, instanceProducer ) : beanRegistry.getBean( typeImplementorClass, instanceProducer ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotationBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotationBinder.java index ab2d437cc3..1ea1c6e3a0 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotationBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotationBinder.java @@ -12,14 +12,30 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import jakarta.persistence.*; -import jakarta.persistence.NamedNativeQueries; -import jakarta.persistence.NamedNativeQuery; -import jakarta.persistence.NamedQueries; -import jakarta.persistence.NamedQuery; import org.hibernate.AnnotationException; import org.hibernate.MappingException; -import org.hibernate.annotations.*; +import org.hibernate.annotations.CollectionTypeRegistration; +import org.hibernate.annotations.CollectionTypeRegistrations; +import org.hibernate.annotations.CompositeTypeRegistration; +import org.hibernate.annotations.CompositeTypeRegistrations; +import org.hibernate.annotations.ConverterRegistration; +import org.hibernate.annotations.ConverterRegistrations; +import org.hibernate.annotations.EmbeddableInstantiatorRegistration; +import org.hibernate.annotations.EmbeddableInstantiatorRegistrations; +import org.hibernate.annotations.FetchProfile; +import org.hibernate.annotations.FetchProfiles; +import org.hibernate.annotations.FilterDef; +import org.hibernate.annotations.FilterDefs; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.GenericGenerators; +import org.hibernate.annotations.Imported; +import org.hibernate.annotations.JavaTypeRegistration; +import org.hibernate.annotations.JavaTypeRegistrations; +import org.hibernate.annotations.JdbcTypeRegistration; +import org.hibernate.annotations.JdbcTypeRegistrations; +import org.hibernate.annotations.ParamDef; +import org.hibernate.annotations.TypeRegistration; +import org.hibernate.annotations.TypeRegistrations; import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.annotations.common.reflection.XAnnotatedElement; import org.hibernate.annotations.common.reflection.XClass; @@ -37,11 +53,12 @@ import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.GenericsHelper; import org.hibernate.internal.util.StringHelper; import org.hibernate.metamodel.mapping.JdbcMapping; -import org.hibernate.type.descriptor.converter.internal.JpaAttributeConverterImpl; +import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; import org.hibernate.resource.beans.spi.ManagedBean; import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.type.BasicType; import org.hibernate.type.CustomType; +import org.hibernate.type.descriptor.converter.internal.JpaAttributeConverterImpl; import org.hibernate.type.descriptor.java.BasicJavaType; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry; @@ -51,10 +68,28 @@ import org.hibernate.type.internal.ConvertedBasicTypeImpl; import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.usertype.UserType; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Entity; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.NamedNativeQueries; +import jakarta.persistence.NamedNativeQuery; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.NamedStoredProcedureQueries; +import jakarta.persistence.NamedStoredProcedureQuery; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.SequenceGenerators; +import jakarta.persistence.SqlResultSetMapping; +import jakarta.persistence.SqlResultSetMappings; +import jakarta.persistence.TableGenerator; +import jakarta.persistence.TableGenerators; + import static java.util.Collections.emptyMap; import static org.hibernate.boot.model.internal.AnnotatedClassType.ENTITY; -import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators; import static org.hibernate.boot.model.internal.BinderHelper.getOverridableAnnotation; +import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators; import static org.hibernate.boot.model.internal.InheritanceState.getInheritanceStateOfSuperEntity; import static org.hibernate.boot.model.internal.InheritanceState.getSuperclassInheritanceState; import static org.hibernate.internal.CoreLogging.messageLogger; @@ -459,7 +494,15 @@ public final class AnnotationBinder { ManagedBeanRegistry managedBeanRegistry, JdbcTypeRegistration annotation) { final Class jdbcTypeClass = annotation.value(); - final JdbcType jdbcType = managedBeanRegistry.getBean( jdbcTypeClass ).getBeanInstance(); + + final JdbcType jdbcType; + if ( context.getBuildingOptions().disallowExtensionsInCdi() ) { + jdbcType = FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( jdbcTypeClass ); + } + else { + jdbcType = managedBeanRegistry.getBean( jdbcTypeClass ).getBeanInstance(); + } + final int typeCode = annotation.registrationCode() == Integer.MIN_VALUE ? jdbcType.getDefaultSqlTypeCode() : annotation.registrationCode(); @@ -470,9 +513,16 @@ public final class AnnotationBinder { MetadataBuildingContext context, ManagedBeanRegistry managedBeanRegistry, JavaTypeRegistration annotation) { - final Class> jtdClass = annotation.descriptorClass(); - final BasicJavaType jtd = managedBeanRegistry.getBean( jtdClass ).getBeanInstance(); - context.getMetadataCollector().addJavaTypeRegistration( annotation.javaType(), jtd ); + final Class> javaTypeClass = annotation.descriptorClass(); + + final BasicJavaType javaType; + if ( context.getBuildingOptions().disallowExtensionsInCdi() ) { + javaType = FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( javaTypeClass ); + } + else { + javaType = managedBeanRegistry.getBean( javaTypeClass ).getBeanInstance(); + } + context.getMetadataCollector().addJavaTypeRegistration( annotation.javaType(), javaType ); } private static void bindEmbeddableInstantiatorRegistrations( @@ -700,11 +750,18 @@ public final class AnnotationBinder { } } - private static JdbcMapping resolveUserType(Class> type, MetadataBuildingContext context) { - final StandardServiceRegistry serviceRegistry = context.getBootstrapContext().getServiceRegistry(); - final ManagedBeanRegistry beanRegistry = serviceRegistry.getService( ManagedBeanRegistry.class ); - final ManagedBean> bean = beanRegistry.getBean( type ); - return new CustomType<>( bean.getBeanInstance(), context.getBootstrapContext().getTypeConfiguration() ); + private static JdbcMapping resolveUserType(Class> userTypeClass, MetadataBuildingContext context) { + final UserType userType; + if ( context.getBuildingOptions().disallowExtensionsInCdi() ) { + userType = FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( userTypeClass ); + } + else { + final StandardServiceRegistry serviceRegistry = context.getBootstrapContext().getServiceRegistry(); + final ManagedBeanRegistry beanRegistry = serviceRegistry.getService( ManagedBeanRegistry.class ); + userType = beanRegistry.getBean( userTypeClass ).getBeanInstance(); + } + + return new CustomType<>( userType, context.getBootstrapContext().getTypeConfiguration() ); } private static JdbcMapping resolveAttributeConverter(Class> type, MetadataBuildingContext context) { @@ -747,18 +804,21 @@ public final class AnnotationBinder { } private static JavaType getJavaType( - Class> type, + Class> javaTypeClass, MetadataBuildingContext context, TypeConfiguration typeConfiguration) { - final JavaType registeredJtd = typeConfiguration.getJavaTypeRegistry().findDescriptor( type ); + final JavaType registeredJtd = typeConfiguration.getJavaTypeRegistry().findDescriptor( javaTypeClass ); if ( registeredJtd != null ) { return registeredJtd; } - else { - final StandardServiceRegistry serviceRegistry = context.getBootstrapContext().getServiceRegistry(); - final ManagedBeanRegistry beanRegistry = serviceRegistry.getService( ManagedBeanRegistry.class ); - return beanRegistry.getBean(type).getBeanInstance(); + + if ( context.getBuildingOptions().disallowExtensionsInCdi() ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( javaTypeClass ); } + + final StandardServiceRegistry serviceRegistry = context.getBootstrapContext().getServiceRegistry(); + final ManagedBeanRegistry beanRegistry = serviceRegistry.getService( ManagedBeanRegistry.class ); + return beanRegistry.getBean(javaTypeClass).getBeanInstance(); } public static void bindFetchProfilesForClass(XClass annotatedClass, MetadataBuildingContext context) { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java index 60bfc7a9cd..1d2328f820 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java @@ -28,7 +28,6 @@ import org.hibernate.annotations.CollectionIdJavaType; import org.hibernate.annotations.CollectionIdJdbcType; import org.hibernate.annotations.CollectionIdJdbcTypeCode; import org.hibernate.annotations.CollectionIdMutability; -import org.hibernate.annotations.PartitionKey; import org.hibernate.annotations.CollectionIdType; import org.hibernate.annotations.Immutable; import org.hibernate.annotations.JdbcTypeCode; @@ -43,6 +42,7 @@ import org.hibernate.annotations.MapKeyType; import org.hibernate.annotations.Mutability; import org.hibernate.annotations.Nationalized; import org.hibernate.annotations.Parameter; +import org.hibernate.annotations.PartitionKey; import org.hibernate.annotations.Target; import org.hibernate.annotations.TimeZoneColumn; import org.hibernate.annotations.TimeZoneStorage; @@ -57,12 +57,12 @@ import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.NationalizationSupport; -import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.Component; import org.hibernate.mapping.Table; +import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; import org.hibernate.resource.beans.spi.ManagedBean; import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.type.BasicType; @@ -430,17 +430,22 @@ public class BasicValueBinder implements JdbcTypeIndicators { throw new MappingException( "idbag mapping missing @CollectionId" ); } + final boolean useDeferredBeanContainerAccess = buildingContext.getBuildingOptions().disallowExtensionsInCdi(); final ManagedBeanRegistry beanRegistry = getManagedBeanRegistry(); explicitBasicTypeName = null; implicitJavaTypeAccess = (typeConfiguration) -> null; explicitJavaTypeAccess = (typeConfiguration) -> { + final CollectionIdJavaType javaTypeAnn = findAnnotation( modelXProperty, CollectionIdJavaType.class ); if ( javaTypeAnn != null ) { - final Class> javaType = normalizeJavaType( javaTypeAnn.value() ); - if ( javaType != null ) { - final ManagedBean> bean = beanRegistry.getBean( javaType ); + final Class> javaTypeClass = normalizeJavaType( javaTypeAnn.value() ); + if ( javaTypeClass != null ) { + if ( useDeferredBeanContainerAccess ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( javaTypeClass ); + } + final ManagedBean> bean = beanRegistry.getBean( javaTypeClass ); return bean.getBeanInstance(); } } @@ -451,9 +456,12 @@ public class BasicValueBinder implements JdbcTypeIndicators { explicitJdbcTypeAccess = (typeConfiguration) -> { final CollectionIdJdbcType jdbcTypeAnn = findAnnotation( modelXProperty, CollectionIdJdbcType.class ); if ( jdbcTypeAnn != null ) { - final Class jdbcType = normalizeJdbcType( jdbcTypeAnn.value() ); - if ( jdbcType != null ) { - final ManagedBean managedBean = beanRegistry.getBean( jdbcType ); + final Class jdbcTypeClass = normalizeJdbcType( jdbcTypeAnn.value() ); + if ( jdbcTypeClass != null ) { + if ( useDeferredBeanContainerAccess ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( jdbcTypeClass ); + } + final ManagedBean managedBean = beanRegistry.getBean( jdbcTypeClass ); return managedBean.getBeanInstance(); } } @@ -471,9 +479,12 @@ public class BasicValueBinder implements JdbcTypeIndicators { explicitMutabilityAccess = (typeConfiguration) -> { final CollectionIdMutability mutabilityAnn = findAnnotation( modelXProperty, CollectionIdMutability.class ); if ( mutabilityAnn != null ) { - final Class> mutability = normalizeMutability( mutabilityAnn.value() ); - if ( mutability != null ) { - final ManagedBean> jtdBean = beanRegistry.getBean( mutability ); + final Class> mutabilityClass = normalizeMutability( mutabilityAnn.value() ); + if ( mutabilityClass != null ) { + if ( useDeferredBeanContainerAccess ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( mutabilityClass ); + } + final ManagedBean> jtdBean = beanRegistry.getBean( mutabilityClass ); return jtdBean.getBeanInstance(); } } @@ -492,6 +503,9 @@ public class BasicValueBinder implements JdbcTypeIndicators { if ( converterDescriptor != null ) { final Mutability converterMutabilityAnn = converterDescriptor.getAttributeConverterClass().getAnnotation( Mutability.class ); if ( converterMutabilityAnn != null ) { + if ( useDeferredBeanContainerAccess ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( converterMutabilityAnn.value() ); + } final ManagedBean> jtdBean = beanRegistry.getBean( converterMutabilityAnn.value() ); return jtdBean.getBeanInstance(); } @@ -537,12 +551,17 @@ public class BasicValueBinder implements JdbcTypeIndicators { temporalPrecision = mapKeyTemporalAnn.value(); } + final boolean useDeferredBeanContainerAccess = buildingContext.getBuildingOptions().disallowExtensionsInCdi(); + explicitJdbcTypeAccess = typeConfiguration -> { final MapKeyJdbcType jdbcTypeAnn = findAnnotation( mapAttribute, MapKeyJdbcType.class ); if ( jdbcTypeAnn != null ) { - final Class jdbcTypeImpl = normalizeJdbcType( jdbcTypeAnn.value() ); - if ( jdbcTypeImpl != null ) { - return getManagedBeanRegistry().getBean( jdbcTypeImpl ).getBeanInstance(); + final Class jdbcTypeClass = normalizeJdbcType( jdbcTypeAnn.value() ); + if ( jdbcTypeClass != null ) { + if ( useDeferredBeanContainerAccess ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( jdbcTypeClass ); + } + return getManagedBeanRegistry().getBean( jdbcTypeClass ).getBeanInstance(); } } @@ -560,9 +579,12 @@ public class BasicValueBinder implements JdbcTypeIndicators { explicitJavaTypeAccess = typeConfiguration -> { final MapKeyJavaType javaTypeAnn = findAnnotation( mapAttribute, MapKeyJavaType.class ); if ( javaTypeAnn != null ) { - final Class> jdbcTypeImpl = normalizeJavaType( javaTypeAnn.value() ); - if ( jdbcTypeImpl != null ) { - return getManagedBeanRegistry().getBean( jdbcTypeImpl ).getBeanInstance(); + final Class> javaTypeClass = normalizeJavaType( javaTypeAnn.value() ); + if ( javaTypeClass != null ) { + if ( useDeferredBeanContainerAccess ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( javaTypeClass ); + } + return getManagedBeanRegistry().getBean( javaTypeClass ).getBeanInstance(); } } @@ -578,9 +600,12 @@ public class BasicValueBinder implements JdbcTypeIndicators { explicitMutabilityAccess = typeConfiguration -> { final MapKeyMutability mutabilityAnn = findAnnotation( mapAttribute, MapKeyMutability.class ); if ( mutabilityAnn != null ) { - final Class> mutability = normalizeMutability( mutabilityAnn.value() ); - if ( mutability != null ) { - final ManagedBean> jtdBean = getManagedBeanRegistry().getBean( mutability ); + final Class> mutabilityClass = normalizeMutability( mutabilityAnn.value() ); + if ( mutabilityClass != null ) { + if ( useDeferredBeanContainerAccess ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( mutabilityClass ); + } + final ManagedBean> jtdBean = getManagedBeanRegistry().getBean( mutabilityClass ); return jtdBean.getBeanInstance(); } } @@ -599,6 +624,9 @@ public class BasicValueBinder implements JdbcTypeIndicators { if ( converterDescriptor != null ) { final Mutability converterMutabilityAnn = converterDescriptor.getAttributeConverterClass().getAnnotation( Mutability.class ); if ( converterMutabilityAnn != null ) { + if ( useDeferredBeanContainerAccess ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( converterMutabilityAnn.value() ); + } final ManagedBean> jtdBean = getManagedBeanRegistry().getBean( converterMutabilityAnn.value() ); return jtdBean.getBeanInstance(); } @@ -623,6 +651,7 @@ public class BasicValueBinder implements JdbcTypeIndicators { private void prepareListIndex(XProperty listAttribute) { implicitJavaTypeAccess = typeConfiguration -> Integer.class; + final boolean useDeferredBeanContainerAccess = buildingContext.getBuildingOptions().disallowExtensionsInCdi(); final ManagedBeanRegistry beanRegistry = buildingContext .getBootstrapContext() .getServiceRegistry() @@ -631,9 +660,12 @@ public class BasicValueBinder implements JdbcTypeIndicators { explicitJavaTypeAccess = (typeConfiguration) -> { final ListIndexJavaType javaTypeAnn = findAnnotation( listAttribute, ListIndexJavaType.class ); if ( javaTypeAnn != null ) { - final Class> javaType = normalizeJavaType( javaTypeAnn.value() ); - if ( javaType != null ) { - final ManagedBean> bean = beanRegistry.getBean( javaType ); + final Class> javaTypeClass = normalizeJavaType( javaTypeAnn.value() ); + if ( javaTypeClass != null ) { + if ( useDeferredBeanContainerAccess ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( javaTypeClass ); + } + final ManagedBean> bean = beanRegistry.getBean( javaTypeClass ); return bean.getBeanInstance(); } } @@ -644,9 +676,12 @@ public class BasicValueBinder implements JdbcTypeIndicators { explicitJdbcTypeAccess = (typeConfiguration) -> { final ListIndexJdbcType jdbcTypeAnn = findAnnotation( listAttribute, ListIndexJdbcType.class ); if ( jdbcTypeAnn != null ) { - final Class jdbcType = normalizeJdbcType( jdbcTypeAnn.value() ); - if ( jdbcType != null ) { - final ManagedBean bean = beanRegistry.getBean( jdbcType ); + final Class jdbcTypeClass = normalizeJdbcType( jdbcTypeAnn.value() ); + if ( jdbcTypeClass != null ) { + if ( useDeferredBeanContainerAccess ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( jdbcTypeClass ); + } + final ManagedBean bean = beanRegistry.getBean( jdbcTypeClass ); return bean.getBeanInstance(); } } @@ -831,13 +866,18 @@ public class BasicValueBinder implements JdbcTypeIndicators { private void prepareAnyKey(XProperty modelXProperty) { implicitJavaTypeAccess = (typeConfiguration) -> null; + final boolean useDeferredBeanContainerAccess = buildingContext.getBuildingOptions().disallowExtensionsInCdi(); + explicitJavaTypeAccess = (typeConfiguration) -> { final AnyKeyJavaType javaTypeAnn = findAnnotation( modelXProperty, AnyKeyJavaType.class ); if ( javaTypeAnn != null ) { - final Class> javaType = normalizeJavaType( javaTypeAnn.value() ); + final Class> javaTypeClass = normalizeJavaType( javaTypeAnn.value() ); - if ( javaType != null ) { - return getManagedBeanRegistry().getBean( javaType ).getBeanInstance(); + if ( javaTypeClass != null ) { + if ( useDeferredBeanContainerAccess ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( javaTypeClass ); + } + return getManagedBeanRegistry().getBean( javaTypeClass ).getBeanInstance(); } } @@ -855,9 +895,12 @@ public class BasicValueBinder implements JdbcTypeIndicators { explicitJdbcTypeAccess = (typeConfiguration) -> { final AnyKeyJdbcType jdbcTypeAnn = findAnnotation( modelXProperty, AnyKeyJdbcType.class ); if ( jdbcTypeAnn != null ) { - final Class jdbcType = normalizeJdbcType( jdbcTypeAnn.value() ); - if ( jdbcType != null ) { - final ManagedBean jtdBean = getManagedBeanRegistry().getBean( jdbcType ); + final Class jdbcTypeClass = normalizeJdbcType( jdbcTypeAnn.value() ); + if ( jdbcTypeClass != null ) { + if ( useDeferredBeanContainerAccess ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( jdbcTypeClass ); + } + final ManagedBean jtdBean = getManagedBeanRegistry().getBean( jdbcTypeClass ); return jtdBean.getBeanInstance(); } } @@ -878,9 +921,12 @@ public class BasicValueBinder implements JdbcTypeIndicators { final org.hibernate.annotations.JdbcType jdbcTypeAnn = findAnnotation( attributeXProperty, org.hibernate.annotations.JdbcType.class ); if ( jdbcTypeAnn != null ) { - final Class jdbcType = normalizeJdbcType( jdbcTypeAnn.value() ); - if ( jdbcType != null ) { - return getManagedBeanRegistry().getBean( jdbcType ).getBeanInstance(); + final Class jdbcTypeClass = normalizeJdbcType( jdbcTypeAnn.value() ); + if ( jdbcTypeClass != null ) { + if ( buildingContext.getBuildingOptions().disallowExtensionsInCdi() ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( jdbcTypeClass ); + } + return getManagedBeanRegistry().getBean( jdbcTypeClass ).getBeanInstance(); } } @@ -903,6 +949,9 @@ public class BasicValueBinder implements JdbcTypeIndicators { if ( mutabilityAnn != null ) { final Class> mutability = normalizeMutability( mutabilityAnn.value() ); if ( mutability != null ) { + if ( buildingContext.getBuildingOptions().disallowExtensionsInCdi() ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( mutability ); + } return getManagedBeanRegistry().getBean( mutability ).getBeanInstance(); } } @@ -926,6 +975,9 @@ public class BasicValueBinder implements JdbcTypeIndicators { if ( converterDescriptor != null ) { final Mutability converterMutabilityAnn = converterDescriptor.getAttributeConverterClass().getAnnotation( Mutability.class ); if ( converterMutabilityAnn != null ) { + if ( buildingContext.getBuildingOptions().disallowExtensionsInCdi() ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( converterMutabilityAnn.value() ); + } final ManagedBean> jtdBean = getManagedBeanRegistry().getBean( converterMutabilityAnn.value() ); return jtdBean.getBeanInstance(); } @@ -952,10 +1004,12 @@ public class BasicValueBinder implements JdbcTypeIndicators { explicitJavaTypeAccess = typeConfiguration -> { final org.hibernate.annotations.JavaType javaTypeAnn = findAnnotation( attributeXProperty, org.hibernate.annotations.JavaType.class ); if ( javaTypeAnn != null ) { - final Class> javaType = normalizeJavaType( javaTypeAnn.value() ); - - if ( javaType != null ) { - final ManagedBean> jtdBean = getManagedBeanRegistry().getBean( javaType ); + final Class> javaTypeClass = normalizeJavaType( javaTypeAnn.value() ); + if ( javaTypeClass != null ) { + if ( buildingContext.getBuildingOptions().disallowExtensionsInCdi() ) { + return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( javaTypeClass ); + } + final ManagedBean> jtdBean = getManagedBeanRegistry().getBean( javaTypeClass ); return jtdBean.getBeanInstance(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java index 5e44ae4710..1cab0abd7e 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java @@ -98,6 +98,7 @@ import org.hibernate.mapping.DependantValue; import org.hibernate.mapping.Join; import org.hibernate.mapping.KeyValue; import org.hibernate.mapping.ManyToOne; +import org.hibernate.mapping.MappingHelper; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.mapping.Selectable; @@ -152,7 +153,6 @@ import static org.hibernate.boot.model.internal.AnnotatedColumn.buildFormulaFrom import static org.hibernate.boot.model.internal.AnnotatedJoinColumns.buildJoinColumnsWithDefaultColumnSuffix; import static org.hibernate.boot.model.internal.AnnotatedJoinColumns.buildJoinTableJoinColumns; import static org.hibernate.boot.model.internal.BinderHelper.buildAnyValue; -import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators; import static org.hibernate.boot.model.internal.BinderHelper.createSyntheticPropertyReference; import static org.hibernate.boot.model.internal.BinderHelper.getCascadeStrategy; import static org.hibernate.boot.model.internal.BinderHelper.getFetchMode; @@ -163,6 +163,7 @@ import static org.hibernate.boot.model.internal.BinderHelper.isPrimitive; import static org.hibernate.boot.model.internal.BinderHelper.toAliasEntityMap; import static org.hibernate.boot.model.internal.BinderHelper.toAliasTableMap; import static org.hibernate.boot.model.internal.EmbeddableBinder.fillEmbeddable; +import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators; import static org.hibernate.boot.model.internal.PropertyHolderBuilder.buildPropertyHolder; import static org.hibernate.cfg.AvailableSettings.USE_ENTITY_WHERE_CLAUSE_FOR_COLLECTIONS; import static org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle.fromResultCheckStyle; @@ -845,37 +846,41 @@ public abstract class CollectionBinder { Class implementation, Map parameters, MetadataBuildingContext buildingContext) { + final boolean hasParameters = CollectionHelper.isNotEmpty( parameters ); + if ( buildingContext.getBuildingOptions().disallowExtensionsInCdi() ) { + // if deferred container access is enabled, we locally create the user-type + return MappingHelper.createLocalUserCollectionTypeBean( role, implementation, hasParameters, parameters ); + } + final ManagedBeanRegistry beanRegistry = buildingContext.getBuildingOptions() .getServiceRegistry() .getService( ManagedBeanRegistry.class ); - if ( CollectionHelper.isNotEmpty( parameters ) ) { - return beanRegistry.getBean( implementation ); - } - else { - // defined parameters... - if ( ParameterizedType.class.isAssignableFrom( implementation ) ) { - // because there are config parameters and the type is configurable, - // we need a separate bean instance which means uniquely naming it - final ManagedBean typeBean = beanRegistry.getBean( role, implementation ); - final UserCollectionType type = typeBean.getBeanInstance(); - final Properties properties = new Properties(); - properties.putAll( parameters ); - ( (ParameterizedType) type ).setParameterValues( properties ); - return typeBean; - } - else { - // log a "warning" - BootLogging.BOOT_LOGGER.debugf( - "Custom collection-type (`%s`) assigned to attribute (`%s`) does not implement `%s`, but its `@CollectionType` defined parameters", - implementation.getName(), - role, - ParameterizedType.class.getName() - ); + final ManagedBean managedBean = beanRegistry.getBean( implementation ); - // but still return the bean - we can again use the no-config bean instance - return beanRegistry.getBean( implementation ); + if ( hasParameters ) { + if ( ParameterizedType.class.isAssignableFrom( managedBean.getBeanClass() ) ) { + // create a copy of the parameters and create a bean wrapper to delay injecting + // the parameters, thereby delaying the need to resolve the instance from the + // wrapped bean + final Properties copy = new Properties(); + copy.putAll( parameters ); + return new DelayedParameterizedTypeBean<>( managedBean, copy ); } + + // there were parameters, but the custom-type does not implement the interface + // used to inject them - log a "warning" + BootLogging.BOOT_LOGGER.debugf( + "`@CollectionType` (%s) specified parameters, but the" + + " implementation does not implement `%s` which is used to inject them - `%s`", + role, + ParameterizedType.class.getName(), + implementation.getName() + ); + + // fall through to returning `managedBean` } + + return managedBean; } private static CollectionBinder createBinderFromProperty(XProperty property, MetadataBuildingContext context) { @@ -902,37 +907,15 @@ public abstract class CollectionBinder { XProperty property, CollectionType typeAnnotation, MetadataBuildingContext context) { - final ManagedBeanRegistry beanRegistry = context.getBootstrapContext() - .getServiceRegistry() - .getService( ManagedBeanRegistry.class ); - final Class typeImpl = typeAnnotation.type(); - if ( typeAnnotation.parameters().length == 0 ) { - // no parameters - we can reuse a no-config bean instance - return beanRegistry.getBean( typeImpl ); - } - else { - // defined parameters... - final String attributeKey = property.getDeclaringClass().getName() + "#" + property.getName(); - if ( ParameterizedType.class.isAssignableFrom( typeImpl ) ) { - // because there are config parameters and the type is configurable, we need - // a separate bean instance which means uniquely naming it - final ManagedBean typeBean = beanRegistry.getBean( attributeKey, typeImpl ); - final UserCollectionType type = typeBean.getBeanInstance(); - ( (ParameterizedType) type ).setParameterValues( extractParameters( typeAnnotation ) ); - return typeBean; - } - else { - // log a "warning" - BootLogging.BOOT_LOGGER.debugf( - "Custom collection-type (`%s`) assigned to attribute (`%s`) does not implement `%s`, but its `@CollectionType` defined parameters", - typeImpl.getName(), - attributeKey, - ParameterizedType.class.getName() - ); - // but still return the bean - we can again use the no-config bean instance - return beanRegistry.getBean( typeImpl ); - } - } + final Properties parameters = extractParameters( typeAnnotation ); + + //noinspection unchecked,rawtypes + return createCustomType( + property.getDeclaringClass().getName() + "." + property.getName(), + typeAnnotation.type(), + (Map) parameters, + context + ); } private static Properties extractParameters(CollectionType typeAnnotation) { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/DelayedParameterizedTypeBean.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/DelayedParameterizedTypeBean.java new file mode 100644 index 0000000000..22a5b2b101 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/DelayedParameterizedTypeBean.java @@ -0,0 +1,74 @@ +/* + * 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.boot.model.internal; + +import java.util.Properties; + +import org.hibernate.boot.BootLogging; +import org.hibernate.internal.util.collections.CollectionHelper; +import org.hibernate.resource.beans.spi.ManagedBean; +import org.hibernate.usertype.ParameterizedType; + +/** + * ManagedBean implementation for delayed {@link ParameterizedType} + * handling (parameter injection) for a UserCollectionType + * + * @author Steve Ebersole + */ +public class DelayedParameterizedTypeBean implements ManagedBean { + private final ManagedBean underlyingBean; + private final Properties properties; + + private T instance; + + public DelayedParameterizedTypeBean(ManagedBean underlyingBean, Properties properties) { + assert ParameterizedType.class.isAssignableFrom( underlyingBean.getBeanClass() ); + this.underlyingBean = underlyingBean; + this.properties = properties; + } + + @Override + public Class getBeanClass() { + return underlyingBean.getBeanClass(); + } + + @Override + public T getBeanInstance() { + if ( instance == null ) { + instance = underlyingBean.getBeanInstance(); + ( (ParameterizedType) instance ).setParameterValues( properties ); + } + return instance; + } + + /** + * Create a bean wrapper which delays parameter injection + * until the bean instance is needed if there are parameters + */ + public static ManagedBean delayedConfigBean( + String role, + ManagedBean bean, + Properties properties) { + if ( CollectionHelper.isNotEmpty( properties ) ) { + if ( ParameterizedType.class.isAssignableFrom( bean.getBeanClass() ) ) { + return new DelayedParameterizedTypeBean<>( bean, properties ); + } + + // there were parameters, but the custom-type does not implement the interface + // used to inject them - log a "warning" + BootLogging.BOOT_LOGGER.debugf( + "`@CollectionType` (%s) specified parameters, but the" + + " implementation does not implement `%s` which is used to inject them - `%s`", + role, + ParameterizedType.class.getName(), + bean.getBeanClass().getName() + ); + } + + return bean; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java index 2620013675..536483bd33 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java @@ -6,17 +6,14 @@ */ package org.hibernate.boot.model.internal; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.Embedded; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToMany; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.MappedSuperclass; -import jakarta.persistence.OneToMany; -import jakarta.persistence.OneToOne; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.hibernate.AnnotationException; import org.hibernate.annotations.Instantiator; import org.hibernate.annotations.TypeBinderType; @@ -36,28 +33,33 @@ import org.hibernate.metamodel.spi.EmbeddableInstantiator; import org.hibernate.property.access.internal.PropertyAccessStrategyCompositeUserTypeImpl; import org.hibernate.property.access.internal.PropertyAccessStrategyMixedImpl; import org.hibernate.property.access.spi.PropertyAccessStrategy; +import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.usertype.CompositeUserType; -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Embedded; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; -import static org.hibernate.boot.model.internal.BinderHelper.isGlobalGeneratorNameGlobal; -import static org.hibernate.boot.model.internal.HCANNHelper.findContainingAnnotations; -import static org.hibernate.boot.model.internal.PropertyBinder.addElementsOfClass; -import static org.hibernate.boot.model.internal.PropertyBinder.processElementAnnotations; -import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators; -import static org.hibernate.boot.model.internal.GeneratorBinder.generatorType; import static org.hibernate.boot.model.internal.BinderHelper.getPath; import static org.hibernate.boot.model.internal.BinderHelper.getPropertyOverriddenByMapperOrMapsId; import static org.hibernate.boot.model.internal.BinderHelper.getRelativePath; import static org.hibernate.boot.model.internal.BinderHelper.hasToOneAnnotation; +import static org.hibernate.boot.model.internal.BinderHelper.isGlobalGeneratorNameGlobal; +import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators; +import static org.hibernate.boot.model.internal.GeneratorBinder.generatorType; import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator; +import static org.hibernate.boot.model.internal.HCANNHelper.findContainingAnnotations; +import static org.hibernate.boot.model.internal.PropertyBinder.addElementsOfClass; +import static org.hibernate.boot.model.internal.PropertyBinder.processElementAnnotations; import static org.hibernate.boot.model.internal.PropertyHolderBuilder.buildPropertyHolder; import static org.hibernate.internal.CoreLogging.messageLogger; import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY; @@ -400,7 +402,12 @@ public class EmbeddableBinder { private static CompositeUserType compositeUserType( Class> compositeUserTypeClass, MetadataBuildingContext context) { - return context.getBootstrapContext().getServiceRegistry() + if ( context.getBuildingOptions().disallowExtensionsInCdi() ) { + FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( compositeUserTypeClass ); + } + + return context.getBootstrapContext() + .getServiceRegistry() .getService( ManagedBeanRegistry.class ) .getBean( compositeUserTypeClass ) .getBeanInstance(); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java index 963d90a654..5cf652a4cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java @@ -146,6 +146,7 @@ import org.hibernate.mapping.Table; import org.hibernate.mapping.UnionSubclass; import org.hibernate.mapping.UniqueKey; import org.hibernate.mapping.Value; +import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; import org.hibernate.resource.beans.spi.ManagedBean; import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.spi.NavigablePath; @@ -2362,13 +2363,18 @@ public class ModelBinder { try { final Class typeJavaType = classLoaderService.classForName( typeName ); - final String beanName = typeName + ":" + TypeDefinition.NAME_COUNTER.getAndIncrement(); - - final ManagedBeanRegistry beanRegistry = bootstrapContext - .getServiceRegistry() - .getService( ManagedBeanRegistry.class ); - final ManagedBean bean = beanRegistry.getBean( beanName, typeJavaType ); - final Object typeInstance = bean.getBeanInstance(); + final Object typeInstance; + if ( metadataBuildingContext.getBuildingOptions().disallowExtensionsInCdi() ) { + typeInstance = FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( typeJavaType ); + } + else { + final ManagedBeanRegistry beanRegistry = bootstrapContext + .getServiceRegistry() + .getService( ManagedBeanRegistry.class ); + final String beanName = typeName + ":" + TypeDefinition.NAME_COUNTER.getAndIncrement(); + final ManagedBean bean = beanRegistry.getBean( beanName, typeJavaType ); + typeInstance = bean.getBeanInstance(); + } if ( typeInstance instanceof ParameterizedType ) { if ( parameters != null ) { @@ -2596,15 +2602,22 @@ public class ModelBinder { log.debugf( "Binding component [%s]", role ); if ( StringHelper.isNotEmpty( explicitComponentClassName ) ) { try { - final Class componentClass = sourceDocument.getBootstrapContext().getClassLoaderAccess() + final Class componentClass = sourceDocument.getBootstrapContext() + .getClassLoaderAccess() .classForName( explicitComponentClassName ); if ( CompositeUserType.class.isAssignableFrom( componentClass ) ) { componentBinding.setTypeName( explicitComponentClassName ); - CompositeUserType compositeUserType = (CompositeUserType) sourceDocument.getBootstrapContext() - .getServiceRegistry() - .getService( ManagedBeanRegistry.class ) - .getBean( componentClass ) - .getBeanInstance(); + CompositeUserType compositeUserType; + if ( sourceDocument.getBuildingOptions().disallowExtensionsInCdi() ) { + compositeUserType = (CompositeUserType) FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( componentClass ); + } + else { + compositeUserType = (CompositeUserType) sourceDocument.getBootstrapContext() + .getServiceRegistry() + .getService( ManagedBeanRegistry.class ) + .getBean( componentClass ) + .getBeanInstance(); + } explicitComponentClassName = compositeUserType.embeddable().getName(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingMetadataBuildingOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingMetadataBuildingOptions.java index 678e6208e4..663e8d7a4a 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingMetadataBuildingOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingMetadataBuildingOptions.java @@ -7,7 +7,7 @@ package org.hibernate.boot.spi; import java.util.List; -import jakarta.persistence.SharedCacheMode; + import org.hibernate.HibernateException; import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.boot.model.IdGeneratorStrategyInterpreter; @@ -21,6 +21,8 @@ import org.hibernate.dialect.TimeZoneSupport; import org.hibernate.id.factory.IdentifierGeneratorFactory; import org.hibernate.type.spi.TypeConfiguration; +import jakarta.persistence.SharedCacheMode; + /** * Convenience base class for custom implementors of {@link MetadataBuildingOptions} using delegation. * @@ -169,4 +171,8 @@ public abstract class AbstractDelegatingMetadataBuildingOptions implements Metad return delegate.isXmlMappingEnabled(); } + @Override + public boolean disallowExtensionsInCdi() { + return delegate.disallowExtensionsInCdi(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java index 3a10da378f..4f03b53245 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java @@ -8,8 +8,6 @@ package org.hibernate.boot.spi; import java.util.List; -import jakarta.persistence.SharedCacheMode; - import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.boot.model.IdGeneratorStrategyInterpreter; import org.hibernate.boot.model.naming.ImplicitNamingStrategy; @@ -27,6 +25,8 @@ import org.hibernate.metamodel.internal.ManagedTypeRepresentationResolverStandar import org.hibernate.metamodel.spi.ManagedTypeRepresentationResolver; import org.hibernate.type.spi.TypeConfiguration; +import jakarta.persistence.SharedCacheMode; + /** * Describes the options used while building the {@link org.hibernate.boot.Metadata} * object during {@link org.hibernate.boot.MetadataBuilder#build()} processing. @@ -231,4 +231,9 @@ public interface MetadataBuildingOptions { default boolean isXmlMappingEnabled() { return true; } + + /** + * Check to see if extensions can be hosted in CDI + */ + boolean disallowExtensionsInCdi(); } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java index 4c21b0c7c1..d2a86f3a87 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java @@ -1308,6 +1308,16 @@ public interface AvailableSettings { */ String DELAY_CDI_ACCESS = "hibernate.delay_cdi_access"; + /** + * Controls whether Hibernate can try to create beans other than converters + * and listeners using CDI. Only meaningful when a CDI {@link #BEAN_CONTAINER container} + * is used. + * + * By default, Hibernate will only attempt to create converter and listener beans using CDI. + * + * @since 6.2 + */ + String ALLOW_EXTENSIONS_IN_CDI = "hibernate.cdi.extensions"; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/id/factory/internal/StandardIdentifierGeneratorFactory.java b/hibernate-core/src/main/java/org/hibernate/id/factory/internal/StandardIdentifierGeneratorFactory.java index 5a12d82780..e7e59b7695 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/factory/internal/StandardIdentifierGeneratorFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/id/factory/internal/StandardIdentifierGeneratorFactory.java @@ -74,7 +74,7 @@ public class StandardIdentifierGeneratorFactory * Constructs a new factory */ public StandardIdentifierGeneratorFactory(ServiceRegistry serviceRegistry) { - this( serviceRegistry, Helper.shouldIgnoreBeanContainer( serviceRegistry ) ); + this( serviceRegistry, !Helper.allowExtensionsInCdi( serviceRegistry ) ); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java b/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java index fba37bd767..ca29347a6b 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java @@ -38,7 +38,7 @@ import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.SelectablePath; -import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; +import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; import org.hibernate.resource.beans.spi.BeanInstanceProducer; import org.hibernate.resource.beans.spi.ManagedBean; import org.hibernate.resource.beans.spi.ManagedBeanRegistry; @@ -46,6 +46,7 @@ import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation; import org.hibernate.type.BasicType; import org.hibernate.type.CustomType; import org.hibernate.type.Type; +import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; import org.hibernate.type.descriptor.java.BasicJavaType; import org.hibernate.type.descriptor.java.BasicPluralJavaType; import org.hibernate.type.descriptor.java.JavaType; @@ -793,18 +794,25 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol properties.putAll( explicitLocalTypeParams ); } - final ManagedBean typeBean; - if ( properties.isEmpty() ) { - typeBean = getServiceRegistry().getService( ManagedBeanRegistry.class ) - .getBean( explicitCustomType, instanceProducer ); + final T typeInstance; + if ( getBuildingContext().getBuildingOptions().disallowExtensionsInCdi() ) { + typeInstance = FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( explicitCustomType ); } else { - final String name = explicitCustomType.getName() + COUNTER++; - typeBean = getServiceRegistry().getService( ManagedBeanRegistry.class ) - .getBean( name, explicitCustomType, instanceProducer ); - } + final boolean hasParameters = CollectionHelper.isNotEmpty( properties ); - final T typeInstance = typeBean.getBeanInstance(); + final ManagedBean typeBean; + if ( hasParameters ) { + final String name = explicitCustomType.getName() + COUNTER++; + typeBean = getServiceRegistry().getService( ManagedBeanRegistry.class ) + .getBean( name, explicitCustomType, instanceProducer ); + } + else { + typeBean = getServiceRegistry().getService( ManagedBeanRegistry.class ) + .getBean( explicitCustomType, instanceProducer ); + } + typeInstance = typeBean.getBeanInstance(); + } if ( typeInstance instanceof TypeConfigurationAware ) { ( (TypeConfigurationAware) typeInstance ).setTypeConfiguration( getTypeConfiguration() ); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/MappingHelper.java b/hibernate-core/src/main/java/org/hibernate/mapping/MappingHelper.java index fb5f825ab9..cf0bbc2696 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/MappingHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/MappingHelper.java @@ -11,12 +11,17 @@ import java.util.Properties; import java.util.function.Supplier; import org.hibernate.Internal; +import org.hibernate.boot.BootLogging; +import org.hibernate.boot.model.internal.DelayedParameterizedTypeBean; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.mapping.MappingModelCreationLogger; +import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; import org.hibernate.resource.beans.spi.ManagedBean; import org.hibernate.resource.beans.spi.ManagedBeanRegistry; +import org.hibernate.resource.beans.spi.ProvidedInstanceManagedBeanImpl; import org.hibernate.type.AnyType; import org.hibernate.type.CollectionType; import org.hibernate.type.CustomCollectionType; @@ -46,23 +51,55 @@ public final class MappingHelper { String propertyRef, MetadataImplementor metadata) { final ClassLoaderService cls = metadata.getMetadataBuildingOptions().getServiceRegistry().getService( ClassLoaderService.class ); - final Class typeImpl = cls.classForName( typeName ); + final Class userCollectionTypeClass = cls.classForName( typeName ); - final ManagedBeanRegistry beanRegistry = metadata - .getMetadataBuildingOptions() - .getServiceRegistry() - .getService( ManagedBeanRegistry.class ); + final boolean hasParameters = CollectionHelper.isNotEmpty( typeParameters ); + final ManagedBean userTypeBean; - final ManagedBean customTypeBean; - if ( typeParameters == null ) { - customTypeBean = beanRegistry.getBean( typeImpl ); + if ( metadata.getMetadataBuildingOptions().disallowExtensionsInCdi() ) { + //noinspection unchecked,rawtypes + userTypeBean = createLocalUserCollectionTypeBean( + role, + userCollectionTypeClass, + hasParameters, + (Map) typeParameters + ); } else { - customTypeBean = beanRegistry.getBean( role, typeImpl ); - injectParameters( customTypeBean.getBeanInstance(), typeParameters ); + final ManagedBeanRegistry beanRegistry = metadata + .getMetadataBuildingOptions() + .getServiceRegistry() + .getService( ManagedBeanRegistry.class ); + final ManagedBean userCollectionTypeBean = beanRegistry.getBean( userCollectionTypeClass ); + + if ( hasParameters ) { + if ( ParameterizedType.class.isAssignableFrom( userCollectionTypeBean.getBeanClass() ) ) { + // create a copy of the parameters and create a bean wrapper to delay injecting + // the parameters, thereby delaying the need to resolve the instance from the + // wrapped bean + final Properties copy = new Properties(); + copy.putAll( typeParameters ); + userTypeBean = new DelayedParameterizedTypeBean<>( userCollectionTypeBean, copy ); + } + else { + // there were parameters, but the custom-type does not implement the interface + // used to inject them - log a "warning" + BootLogging.BOOT_LOGGER.debugf( + "`@CollectionType` (%s) specified parameters, but the" + + " implementation does not implement `%s` which is used to inject them - `%s`", + role, + ParameterizedType.class.getName(), + userCollectionTypeClass.getName() + ); + userTypeBean = userCollectionTypeBean; + } + } + else { + userTypeBean = userCollectionTypeBean; + } } - return new CustomCollectionType( customTypeBean, role, propertyRef ); + return new CustomCollectionType( userTypeBean, role, propertyRef ); } public static void injectParameters(Object type, Properties parameters) { @@ -166,4 +203,36 @@ public final class MappingHelper { constrained ); } + + public static ManagedBean createLocalUserCollectionTypeBean( + String role, + Class implementation, + boolean hasParameters, + Map parameters) { + final UserCollectionType userCollectionType = FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( implementation ); + + if ( hasParameters ) { + // `@CollectionType` declared parameters - inject them + if ( userCollectionType instanceof ParameterizedType ) { + final Properties properties = new Properties(); + properties.putAll( parameters ); + ( (ParameterizedType) userCollectionType ).setParameterValues( properties ); + } + else { + // there were parameters, but the custom-type does not implement the interface + // used to inject them - log a "warning" + BootLogging.BOOT_LOGGER.debugf( + "`@CollectionType` (%s) specified parameters, but the" + + " implementation does not implement `%s` which is used to inject them - `%s`", + role, + ParameterizedType.class.getName(), + implementation.getName() + ); + + // use the un-configured instance + } + } + + return new ProvidedInstanceManagedBeanImpl<>( userCollectionType ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/ManagedTypeRepresentationResolverStandard.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/ManagedTypeRepresentationResolverStandard.java index 1b7ac85b17..a43cf1b666 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/ManagedTypeRepresentationResolverStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/ManagedTypeRepresentationResolverStandard.java @@ -20,6 +20,7 @@ import org.hibernate.metamodel.spi.EntityRepresentationStrategy; import org.hibernate.metamodel.spi.ManagedTypeRepresentationResolver; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.usertype.CompositeUserType; @@ -78,32 +79,42 @@ public class ManagedTypeRepresentationResolverStandard implements ManagedTypeRep } } - final CompositeUserType compositeUserType; + final CompositeUserType compositeUserType; if ( bootDescriptor.getTypeName() != null ) { - compositeUserType = (CompositeUserType) creationContext.getBootstrapContext() - .getServiceRegistry() - .getService( ManagedBeanRegistry.class ) - .getBean( - creationContext.getBootstrapContext() - .getClassLoaderAccess() - .classForName( bootDescriptor.getTypeName() ) - ) - .getBeanInstance(); + final Class> userTypeClass = creationContext.getBootstrapContext() + .getClassLoaderAccess() + .classForName( bootDescriptor.getTypeName() ); + if ( creationContext.getBootModel().getMetadataBuildingOptions().disallowExtensionsInCdi() ) { + compositeUserType = FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( userTypeClass ); + } + else { + compositeUserType = creationContext.getBootstrapContext() + .getServiceRegistry() + .getService( ManagedBeanRegistry.class ) + .getBean( userTypeClass ) + .getBeanInstance(); + } } else { compositeUserType = null; } final EmbeddableInstantiator customInstantiator; if ( bootDescriptor.getCustomInstantiator() != null ) { - final Class customInstantiatorImpl = bootDescriptor.getCustomInstantiator(); - customInstantiator = creationContext.getBootstrapContext() - .getServiceRegistry() - .getService( ManagedBeanRegistry.class ) - .getBean( customInstantiatorImpl ) - .getBeanInstance(); + final Class instantiatorClass = bootDescriptor.getCustomInstantiator(); + if ( creationContext.getBootModel().getMetadataBuildingOptions().disallowExtensionsInCdi() ) { + customInstantiator = FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( instantiatorClass ); + } + else { + customInstantiator = creationContext.getBootstrapContext() + .getServiceRegistry() + .getService( ManagedBeanRegistry.class ) + .getBean( instantiatorClass ) + .getBeanInstance(); + } } else if ( compositeUserType != null ) { - customInstantiator = new EmbeddableCompositeUserTypeInstantiator( compositeUserType ); + //noinspection unchecked,rawtypes + customInstantiator = new EmbeddableCompositeUserTypeInstantiator( (CompositeUserType) compositeUserType ); } else if ( bootDescriptor.getComponentClassName() != null && ReflectHelper.isRecord( bootDescriptor.getComponentClass() ) ) { if ( bootDescriptor.sortProperties() == null ) { @@ -143,11 +154,12 @@ public class ManagedTypeRepresentationResolverStandard implements ManagedTypeRep // // instead, resolve ReflectionOptimizer once - here - and pass along to // StandardPojoRepresentationStrategy + //noinspection unchecked return new EmbeddableRepresentationStrategyPojo( bootDescriptor, runtimeDescriptorAccess, customInstantiator, - compositeUserType, + (CompositeUserType) compositeUserType, creationContext ); } diff --git a/hibernate-core/src/main/java/org/hibernate/resource/beans/internal/Helper.java b/hibernate-core/src/main/java/org/hibernate/resource/beans/internal/Helper.java index 61d83996cd..a396c82fb5 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/beans/internal/Helper.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/beans/internal/Helper.java @@ -9,10 +9,9 @@ package org.hibernate.resource.beans.internal; import org.hibernate.cfg.AvailableSettings; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.StandardConverters; -import org.hibernate.resource.beans.container.spi.BeanLifecycleStrategy; import org.hibernate.resource.beans.container.internal.ContainerManagedLifecycleStrategy; import org.hibernate.resource.beans.container.internal.JpaCompliantLifecycleStrategy; -import org.hibernate.resource.beans.container.spi.ExtendedBeanManager; +import org.hibernate.resource.beans.container.spi.BeanLifecycleStrategy; import org.hibernate.service.ServiceRegistry; /** @@ -31,19 +30,13 @@ public final class Helper { return beanType.getName() + ':' + name; } - public static boolean shouldIgnoreBeanContainer(ServiceRegistry serviceRegistry) { + public static boolean allowExtensionsInCdi(ServiceRegistry serviceRegistry) { final ConfigurationService configService = serviceRegistry.getService( ConfigurationService.class ); - final Object beanManagerRef = configService.getSettings().get( AvailableSettings.JAKARTA_CDI_BEAN_MANAGER ); - - if ( beanManagerRef instanceof ExtendedBeanManager ) { - return true; - } - - if ( configService.getSetting( AvailableSettings.DELAY_CDI_ACCESS, StandardConverters.BOOLEAN, false ) ) { - return true; - } - - return false; + return configService.getSetting( + AvailableSettings.ALLOW_EXTENSIONS_IN_CDI, + StandardConverters.BOOLEAN, + false + ); } @SuppressWarnings("unused") diff --git a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java index 7661a07e85..9d733d3768 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java @@ -36,6 +36,7 @@ import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.spi.EmbeddableInstantiator; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.sqm.SqmExpressible; +import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.spi.CompositeTypeImplementor; @@ -96,11 +97,17 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen final ManagedBeanRegistry beanRegistry = buildingContext.getBootstrapContext() .getServiceRegistry() .getService( ManagedBeanRegistry.class ); - final Class customTypeClass = buildingContext.getBootstrapContext() + final Class> customTypeClass = buildingContext.getBootstrapContext() .getClassLoaderAccess() .classForName( component.getTypeName() ); - //noinspection unchecked - this.compositeUserType = (CompositeUserType) beanRegistry.getBean( customTypeClass ).getBeanInstance(); + if ( buildingContext.getBuildingOptions().disallowExtensionsInCdi() ) { + //noinspection unchecked,rawtypes + this.compositeUserType = (CompositeUserType) FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( customTypeClass ); + } + else { + //noinspection unchecked,rawtypes + this.compositeUserType = (CompositeUserType) beanRegistry.getBean( customTypeClass ).getBeanInstance(); + } } else { this.compositeUserType = null; diff --git a/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java b/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java index 9681fbc2ee..ceb8827697 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java +++ b/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java @@ -52,11 +52,12 @@ import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.model.domain.internal.ArrayTupleType; +import org.hibernate.query.internal.QueryHelper; import org.hibernate.query.sqm.BinaryArithmeticOperator; import org.hibernate.query.sqm.IntervalType; -import org.hibernate.query.internal.QueryHelper; import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.tree.SqmTypedNode; +import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; import org.hibernate.resource.beans.spi.ManagedBean; import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.service.ServiceRegistry; @@ -401,6 +402,7 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable { private transient MetadataBuildingContext metadataBuildingContext; private transient SessionFactoryImplementor sessionFactory; + private boolean allowExtensionsInCdi; private String sessionFactoryName; private String sessionFactoryUuid; @@ -491,6 +493,9 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable { private void setMetadataBuildingContext(MetadataBuildingContext metadataBuildingContext) { this.metadataBuildingContext = metadataBuildingContext; + if ( metadataBuildingContext != null ) { + this.allowExtensionsInCdi = metadataBuildingContext.getBuildingOptions().disallowExtensionsInCdi(); + } } private SessionFactoryImplementor getSessionFactory() { @@ -805,8 +810,12 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable { @Internal @SuppressWarnings("unchecked") public MutabilityPlan createMutabilityPlan(Class> planClass) { - final ManagedBean> planBean = - scope.getServiceRegistry() + if ( scope.allowExtensionsInCdi ) { + //noinspection rawtypes + return (MutabilityPlan) FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( planClass ); + } + + final ManagedBean> planBean = scope.getServiceRegistry() .getService( ManagedBeanRegistry.class ) .getBean( planClass ); return (MutabilityPlan) planBean.getBeanInstance(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/CdiSmokeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/CdiSmokeTests.java new file mode 100644 index 0000000000..a6042824a7 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/CdiSmokeTests.java @@ -0,0 +1,56 @@ +/* + * 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.orm.test.cdi.type; + +import org.hibernate.resource.beans.container.internal.NotYetReadyException; + +import org.junit.jupiter.api.Test; + +import jakarta.enterprise.context.spi.CreationalContext; +import jakarta.enterprise.inject.se.SeContainer; +import jakarta.enterprise.inject.se.SeContainerInitializer; +import jakarta.enterprise.inject.spi.AnnotatedType; +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.enterprise.inject.spi.InjectionTarget; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +public class CdiSmokeTests { + @Test + void testCdiOperations() { + final SeContainerInitializer cdiInitializer = SeContainerInitializer.newInstance() + .disableDiscovery() + .addBeanClasses( UrlType.class, OtherBean.class ); + try ( final SeContainer cdiContainer = cdiInitializer.initialize() ) { + final BeanManager beanManager = cdiContainer.getBeanManager(); + + final AnnotatedType annotatedType; + try { + annotatedType = beanManager.createAnnotatedType( UrlType.class ); + } + catch (Exception e) { + throw new IllegalStateException( new NotYetReadyException( e ) ); + } + + final InjectionTarget injectionTarget = beanManager + .getInjectionTargetFactory( annotatedType ) + .createInjectionTarget( null ); + final CreationalContext creationalContext = beanManager.createCreationalContext( null ); + + final UrlType beanInstance = injectionTarget.produce( creationalContext ); + injectionTarget.inject( beanInstance, creationalContext ); + + injectionTarget.postConstruct( beanInstance ); + + assertThat( beanInstance ).isNotNull(); +// assertThat( beanInstance.getOtherBean() ).isNotNull(); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/ExtendedBeanManagerImpl.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/ExtendedBeanManagerImpl.java new file mode 100644 index 0000000000..2da3e6a360 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/ExtendedBeanManagerImpl.java @@ -0,0 +1,27 @@ +/* + * 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.orm.test.cdi.type; + +import org.hibernate.resource.beans.container.spi.ExtendedBeanManager; + +import jakarta.enterprise.inject.spi.BeanManager; + +/** + * @author Steve Ebersole + */ +public class ExtendedBeanManagerImpl implements ExtendedBeanManager { + private LifecycleListener lifecycleListener; + + @Override + public void registerLifecycleListener(LifecycleListener lifecycleListener) { + this.lifecycleListener = lifecycleListener; + } + + public void injectBeanManager(BeanManager beanManager) { + lifecycleListener.beanManagerInitialized( beanManager ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/OtherBean.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/OtherBean.java new file mode 100644 index 0000000000..132ee1e9bf --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/OtherBean.java @@ -0,0 +1,13 @@ +/* + * 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.orm.test.cdi.type; + +/** + * @author Steve Ebersole + */ +public class OtherBean { +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/SimpleTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/SimpleTests.java new file mode 100644 index 0000000000..ab475e697f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/SimpleTests.java @@ -0,0 +1,120 @@ +/* + * 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.orm.test.cdi.type; + +import java.net.URL; + +import org.hibernate.SessionFactory; +import org.hibernate.annotations.Type; +import org.hibernate.boot.Metadata; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.model.process.internal.UserTypeResolution; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.mapping.BasicValue; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.tool.schema.Action; + +import org.junit.jupiter.api.Test; + +import jakarta.enterprise.inject.se.SeContainer; +import jakarta.enterprise.inject.se.SeContainerInitializer; +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +public class SimpleTests { + @Test + void testProperUsage() { + final ExtendedBeanManagerImpl extendedBeanManager = new ExtendedBeanManagerImpl(); + + final StandardServiceRegistryBuilder ssrbBuilder = new StandardServiceRegistryBuilder() + .applySetting( AvailableSettings.HBM2DDL_AUTO, Action.CREATE_DROP ) + .applySetting( AvailableSettings.JAKARTA_CDI_BEAN_MANAGER, extendedBeanManager ); + + try ( final StandardServiceRegistry ssr = ssrbBuilder.build() ) { + final Metadata metadata = new MetadataSources( ssr ) + .addAnnotatedClass( MappedEntity.class ) + .buildMetadata(); + + final PersistentClass entityBinding = metadata.getEntityBinding( MappedEntity.class.getName() ); + final Property property = entityBinding.getProperty( "url" ); + assertThat( property ).isNotNull(); + assertThat( property.getValue() ).isInstanceOf( BasicValue.class ); + final BasicValue.Resolution resolution = ( (BasicValue) property.getValue() ).getResolution(); + assertThat( resolution ).isNotNull(); + assertThat( resolution ).isInstanceOf( UserTypeResolution.class ); +// assertThat( ( (UserTypeResolution) resolution ).isResolved() ).isFalse(); + + final SeContainerInitializer cdiInitializer = SeContainerInitializer.newInstance() + .disableDiscovery() + .addBeanClasses( UrlType.class, OtherBean.class ); + try ( final SeContainer cdiContainer = cdiInitializer.initialize() ) { + final BeanManager beanManager = cdiContainer.getBeanManager(); + extendedBeanManager.injectBeanManager( beanManager ); + } + + try ( final SessionFactory sf = metadata.buildSessionFactory() ) { + sf.inSession( (session) -> { + session.createSelectionQuery( "from MappedEntity" ).list(); + } ); + } + } + + } + + @Entity( name = "MappedEntity" ) + @Table( name = "mapped_entity" ) + public static class MappedEntity { + @Id + private Integer id; + @Basic + private String name; + @Basic + @Type( UrlType.class ) + private URL url; + + protected MappedEntity() { + // for use by Hibernate + } + + public MappedEntity(Integer id, String name, URL url) { + this.id = id; + this.name = name; + this.url = url; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public URL getUrl() { + return url; + } + + public void setUrl(URL url) { + this.url = url; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/UrlType.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/UrlType.java new file mode 100644 index 0000000000..8286e005b1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/type/UrlType.java @@ -0,0 +1,91 @@ +/* + * 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.orm.test.cdi.type; + +import java.io.Serializable; +import java.net.URL; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.type.SqlTypes; +import org.hibernate.usertype.UserType; + +import jakarta.inject.Singleton; +import org.assertj.core.util.Objects; + +/** + * @author Steve Ebersole + */ +@Singleton +public class UrlType implements UserType { +// because we cannot use CDI, injection is not available +// private final OtherBean otherBean; +// +// @Inject +// public UrlType(OtherBean otherBean) { +// if ( otherBean == null ) { +// throw new UnsupportedOperationException( "OtherBean cannot be null" ); +// } +// this.otherBean = otherBean; +// } +// +// public OtherBean getOtherBean() { +// return otherBean; +// } + + @Override + public int getSqlType() { + return SqlTypes.VARCHAR; + } + + @Override + public Class returnedClass() { + return URL.class; + } + + @Override + public boolean equals(URL x, URL y) { + return Objects.areEqual( x, y ); + } + + @Override + public int hashCode(URL x) { + return Objects.hashCodeFor( x ); + } + + @Override + public URL nullSafeGet(ResultSet rs, int position, SharedSessionContractImplementor session, Object owner) throws SQLException { + throw new UnsupportedOperationException( "Not used" ); + } + + @Override + public void nullSafeSet(PreparedStatement st, URL value, int index, SharedSessionContractImplementor session) throws SQLException { + throw new UnsupportedOperationException( "Not used" ); + } + + @Override + public URL deepCopy(URL value) { + throw new UnsupportedOperationException( "Not used" ); + } + + @Override + public boolean isMutable() { + return false; + } + + @Override + public Serializable disassemble(URL value) { + throw new UnsupportedOperationException( "Not used" ); + } + + @Override + public URL assemble(Serializable cached, Object owner) { + throw new UnsupportedOperationException( "Not used" ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/UserDefinedGeneratorsTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/UserDefinedGeneratorsTests.java index cb13a9a07d..914c0e96cb 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/UserDefinedGeneratorsTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/UserDefinedGeneratorsTests.java @@ -6,23 +6,9 @@ */ package org.hibernate.orm.test.idgen.userdefined; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.then; -import static org.mockito.Mockito.times; - import java.io.Serializable; import java.util.concurrent.atomic.AtomicInteger; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.Table; - import org.hibernate.annotations.GenericGenerator; import org.hibernate.boot.Metadata; import org.hibernate.boot.MetadataSources; @@ -39,13 +25,26 @@ import org.hibernate.resource.beans.container.spi.BeanContainer; import org.hibernate.resource.beans.container.spi.BeanContainer.LifecycleOptions; import org.hibernate.resource.beans.container.spi.ContainedBean; import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; + import org.hibernate.testing.TestForIssue; import org.hibernate.testing.orm.junit.BaseUnitTest; - import org.junit.jupiter.api.Test; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Table; import org.mockito.Mockito; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.times; + /** * @author Yanming Zhou */ @@ -65,7 +64,8 @@ public class UserDefinedGeneratorsTests { } ); final StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder(); - ssrb.applySetting( AvailableSettings.BEAN_CONTAINER, beanContainer ); + ssrb.applySetting( AvailableSettings.BEAN_CONTAINER, beanContainer ) + .applySetting( AvailableSettings.ALLOW_EXTENSIONS_IN_CDI, "true" ); try (final StandardServiceRegistry ssr = ssrb.build()) { final Metadata metadata = new MetadataSources( ssr ) diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/ComponentMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/ComponentMetadataGenerator.java index 4cf51c785f..35a107a76d 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/ComponentMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/ComponentMetadataGenerator.java @@ -21,6 +21,7 @@ import org.hibernate.mapping.Value; import org.hibernate.metamodel.internal.EmbeddableCompositeUserTypeInstantiator; import org.hibernate.metamodel.internal.EmbeddableInstantiatorPojoIndirecting; import org.hibernate.metamodel.spi.EmbeddableInstantiator; +import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.usertype.CompositeUserType; @@ -52,23 +53,33 @@ public final class ComponentMetadataGenerator extends AbstractMetadataGenerator final Component propComponent = (Component) value; final EmbeddableInstantiator instantiator; if ( propComponent.getCustomInstantiator() != null ) { - instantiator = getMetadataBuildingContext().getBootstrapContext() + if ( getMetadataBuildingContext().getBuildingOptions().disallowExtensionsInCdi() ) { + instantiator = FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( propComponent.getCustomInstantiator() ); + } + else + instantiator = getMetadataBuildingContext().getBootstrapContext() .getServiceRegistry() .getService( ManagedBeanRegistry.class ) .getBean( propComponent.getCustomInstantiator() ) .getBeanInstance(); } else if ( propComponent.getTypeName() != null ) { - final CompositeUserType compositeUserType = (CompositeUserType) getMetadataBuildingContext().getBootstrapContext() - .getServiceRegistry() - .getService( ManagedBeanRegistry.class ) - .getBean( - getMetadataBuildingContext().getBootstrapContext() - .getClassLoaderAccess() - .classForName( propComponent.getTypeName() ) - ) - .getBeanInstance(); - instantiator = new EmbeddableCompositeUserTypeInstantiator( compositeUserType ); + final Class> userTypeClass = getMetadataBuildingContext().getBootstrapContext() + .getClassLoaderAccess() + .classForName( propComponent.getTypeName() ); + if ( getMetadataBuildingContext().getBuildingOptions().disallowExtensionsInCdi() ) { + final CompositeUserType compositeUserType = FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( userTypeClass ); + //noinspection rawtypes + instantiator = new EmbeddableCompositeUserTypeInstantiator( (CompositeUserType) compositeUserType ); + } + else { + final CompositeUserType compositeUserType = (CompositeUserType) getMetadataBuildingContext().getBootstrapContext() + .getServiceRegistry() + .getService( ManagedBeanRegistry.class ) + .getBean( userTypeClass ) + .getBeanInstance(); + instantiator = new EmbeddableCompositeUserTypeInstantiator( compositeUserType ); + } } else if ( propComponent.getInstantiator() != null ) { instantiator = EmbeddableInstantiatorPojoIndirecting.of( diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/revisioninfo/DefaultRevisionInfoGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/revisioninfo/DefaultRevisionInfoGenerator.java index b01753b5b9..44b822336f 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/revisioninfo/DefaultRevisionInfoGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/revisioninfo/DefaultRevisionInfoGenerator.java @@ -15,8 +15,11 @@ import org.hibernate.envers.RevisionType; import org.hibernate.envers.exception.AuditException; import org.hibernate.envers.internal.synchronization.SessionCacheCleaner; import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; +import org.hibernate.resource.beans.internal.Helper; import org.hibernate.resource.beans.spi.ManagedBean; import org.hibernate.resource.beans.spi.ManagedBeanRegistry; +import org.hibernate.resource.beans.spi.ProvidedInstanceManagedBeanImpl; import org.hibernate.service.ServiceRegistry; /** @@ -107,6 +110,11 @@ public class DefaultRevisionInfoGenerator implements RevisionInfoGenerator { Class listenerClass, ServiceRegistry serviceRegistry) { if ( !listenerClass.equals( RevisionListener.class ) ) { + if ( Helper.allowExtensionsInCdi( serviceRegistry ) ) { + return new ProvidedInstanceManagedBeanImpl<>( + FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( listenerClass ) + ); + } return serviceRegistry.getService( ManagedBeanRegistry.class ).getBean( listenerClass ); } return null;