diff --git a/hibernate-core/src/main/java/org/hibernate/resource/beans/container/internal/JpaCompliantLifecycleStrategy.java b/hibernate-core/src/main/java/org/hibernate/resource/beans/container/internal/JpaCompliantLifecycleStrategy.java index 20ca55aeff..2b48c40f6e 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/beans/container/internal/JpaCompliantLifecycleStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/beans/container/internal/JpaCompliantLifecycleStrategy.java @@ -223,6 +223,20 @@ public class JpaCompliantLifecycleStrategy implements BeanLifecycleStrategy { return; } + if ( beanManager == null ) { + try { + beanInstance = fallbackProducer.produceBeanInstance( beanType ); + return; + } + catch (Exception e) { + // the CDI BeanManager is not know to be ready for use and the + // fallback-producer was unable to create the bean... + throw new IllegalStateException( + "CDI BeanManager is not known to be ready for use and the fallback-producer was unable to create the bean", + new NotYetReadyException( e ) + ); + } + } try { this.creationalContext = beanManager.createCreationalContext( null ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/lifecycle/ExtendedBeanManagerNotAvailableDuringCustomUserTypeInitTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/lifecycle/ExtendedBeanManagerNotAvailableDuringCustomUserTypeInitTest.java new file mode 100644 index 0000000000..b405179518 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cdi/lifecycle/ExtendedBeanManagerNotAvailableDuringCustomUserTypeInitTest.java @@ -0,0 +1,153 @@ +/* + * 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.lifecycle; + +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import org.hibernate.HibernateException; +import org.hibernate.annotations.Type; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.resource.beans.container.spi.ExtendedBeanManager; +import org.hibernate.testing.TestForIssue; +import org.hibernate.usertype.DynamicParameterizedType; +import org.hibernate.usertype.UserType; +import org.junit.jupiter.api.Test; + +import java.io.Serializable; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Properties; + +@TestForIssue(jiraKey = "HHH-16096") +public class ExtendedBeanManagerNotAvailableDuringCustomUserTypeInitTest { + + @Test + public void tryIt() { + // pass ExtendedBeanManager but never initialize it + final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder() + .applySetting( AvailableSettings.JAKARTA_CDI_BEAN_MANAGER, new ExtendedBeanManagerImpl() ) + .build(); + + // this will trigger initialization of dynamic parameterized user type bean + //noinspection EmptyTryBlock + try (var ignored = new MetadataSources(ssr) + .addAnnotatedClass(TheEntity.class) + .buildMetadata() + .buildSessionFactory()) { + } + finally { + StandardServiceRegistryBuilder.destroy(ssr); + } + } + + @SuppressWarnings("JpaDataSourceORMInspection") + @Entity(name = "TheEntity") + @Table(name = "TheEntity") + public static class TheEntity { + @Id + private Integer id; + private String name; + + @Type(CustomDynamicParameterizedUserType.class) + private String custom; + } + + public static class CustomDynamicParameterizedUserType implements UserType, DynamicParameterizedType { + + @Override + public int getSqlType() { + return Types.VARCHAR; + } + + @Override + public Class returnedClass() { + return Object.class; + } + + @Override + public boolean equals(Object x, Object y) throws HibernateException { + if (x == null) { + return y == null; + } else { + return x.equals(y); + } + } + + @Override + public int hashCode(Object x) throws HibernateException { + return x == null ? 0 : x.hashCode(); + } + + @Override + public Object nullSafeGet(ResultSet rs, int i, SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws SQLException { + String xmldoc = rs.getString(i); + return rs.wasNull() ? null : xmldoc; + } + + @Override + public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException { + if (value == null) { + st.setNull(index, Types.OTHER); + } else { + st.setObject(index, value, Types.OTHER); + } + } + + @Override + public Object deepCopy(Object value) throws HibernateException { + return value; + } + + @Override + public boolean isMutable() { + return false; + } + + @Override + public Serializable disassemble(Object value) throws HibernateException { + return (String) value; + } + + @Override + public Object assemble(Serializable cached, Object owner) throws HibernateException { + return cached; + } + + @Override + public Object replace(Object original, Object target, Object owner) throws HibernateException { + return original; + } + + @Override + public void setParameterValues(Properties parameters) { + // nothing + } + } + + public static class ExtendedBeanManagerImpl implements ExtendedBeanManager { + private LifecycleListener lifecycleListener; + + @Override + public void registerLifecycleListener(LifecycleListener lifecycleListener) { + assert this.lifecycleListener == null; + this.lifecycleListener = lifecycleListener; + } + + // not called + public void notify(BeanManager ready) { + lifecycleListener.beanManagerInitialized( ready ); + } + } +}