HHH-12133 - Create ManagedBeanRegistry and ManagedBean

- Add non-regression tests related to the fallback bean instance producer
 - Fix a bug where we would call beanInstanceProducer.produceBeanInstance(Class) even when a named bean was requested.
 - Do not call Instance#destroy on bean instances produced by the fallback bean producer.  We used to, because we expected the Instance resolution to fail when a bean could not be found. But Instance resolution will never fail in that case, it will just return an Instance whose #isUnsatisfied() method returns true. It happens that calling Instance#destroy did not fail with Weld, because Weld just ignores the call in that case, but other CDI implementations may behave differently: the javadoc, and probably also the CDI spec, are not explicit about what happens in that case.
This commit is contained in:
Yoann Rodière 2018-01-17 13:11:02 +01:00 committed by Steve Ebersole
parent df101b0b2e
commit c52b8389f2
8 changed files with 173 additions and 19 deletions

View File

@ -111,7 +111,8 @@ public class ContainerManagedLifecycleStrategy implements BeanLifecycleStrategy
}
catch (Exception e) {
log.debugf( "Error resolving CDI bean [%s] - using fallback" );
this.beanInstance = fallbackProducer.produceBeanInstance( beanType );
this.beanInstance = produceFallbackInstance();
this.instance = null;
}
this.beanManager = null;
@ -148,6 +149,8 @@ public class ContainerManagedLifecycleStrategy implements BeanLifecycleStrategy
fallbackProducer = null;
}
}
protected abstract B produceFallbackInstance();
}
private static class BeanImpl<B> extends AbstractBeanImpl<B> {
@ -178,6 +181,11 @@ public class ContainerManagedLifecycleStrategy implements BeanLifecycleStrategy
throw new NoSuchBeanException( "Bean class not known to CDI : " + beanType.getName(), e );
}
}
@Override
protected B produceFallbackInstance() {
return fallbackProducer.produceBeanInstance( beanType );
}
}
private static class NamedBeanImpl<B> extends AbstractBeanImpl<B> {
@ -212,5 +220,10 @@ public class ContainerManagedLifecycleStrategy implements BeanLifecycleStrategy
throw new NoSuchBeanException( "Bean class not known to CDI : " + beanType.getName(), e );
}
}
@Override
protected B produceFallbackInstance() {
return fallbackProducer.produceBeanInstance( beanName, beanType );
}
}
}

View File

@ -233,7 +233,7 @@ public class JpaCompliantLifecycleStrategy implements BeanLifecycleStrategy {
}
catch (Exception e) {
log.debugf( "Error resolving CDI bean [%s] - using fallback" );
this.beanInstance = fallbackProducer.produceBeanInstance( beanType );
this.beanInstance = fallbackProducer.produceBeanInstance( beanName, beanType );
try {
if ( this.creationalContext != null ) {

View File

@ -14,6 +14,7 @@ import org.hibernate.resource.beans.container.spi.BeanContainer;
import org.hibernate.resource.beans.container.spi.ContainedBeanImplementor;
import org.hibernate.resource.beans.container.spi.ExtendedBeanManager;
import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer;
import org.hibernate.resource.beans.spi.BeanInstanceProducer;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
@ -28,15 +29,25 @@ import static org.hamcrest.MatcherAssert.assertThat;
* @author Yoann Rodiere
*/
public class NonRegistryManagedBeanConsumingIntegrator implements Integrator {
private final BeanInstanceProducer fallbackBeanInstanceProducer;
private ContainedBeanImplementor<TheApplicationScopedBean> applicationScopedBean1;
private ContainedBeanImplementor<TheApplicationScopedBean> applicationScopedBean2;
private ContainedBeanImplementor<TheDependentBean> dependentBean1;
private ContainedBeanImplementor<TheDependentBean> dependentBean2;
private ContainedBeanImplementor<TheReflectionInstantiatedBean> reflectionInstantiatedBean1;
private ContainedBeanImplementor<TheReflectionInstantiatedBean> reflectionInstantiatedBean2;
private ContainedBeanImplementor<TheNamedApplicationScopedBean> namedApplicationScopedBean1;
private ContainedBeanImplementor<TheNamedApplicationScopedBean> namedApplicationScopedBean2;
private ContainedBeanImplementor<TheNamedDependentBean> namedDependentBean1;
private ContainedBeanImplementor<TheNamedDependentBean> namedDependentBean2;
private ContainedBeanImplementor<TheReflectionInstantiatedBean> namedReflectionInstantiatedBean1;
private ContainedBeanImplementor<TheReflectionInstantiatedBean> namedReflectionInstantiatedBean2;
public NonRegistryManagedBeanConsumingIntegrator(BeanInstanceProducer fallbackBeanInstanceProducer) {
this.fallbackBeanInstanceProducer = fallbackBeanInstanceProducer;
}
@Override
@SuppressWarnings("unchecked")
@ -50,46 +61,68 @@ public class NonRegistryManagedBeanConsumingIntegrator implements Integrator {
applicationScopedBean1 = (ContainedBeanImplementor) beanContainer.getBean(
TheApplicationScopedBean.class,
ContainerManagedLifecycleStrategy.INSTANCE,
FallbackBeanInstanceProducer.INSTANCE
fallbackBeanInstanceProducer
);
applicationScopedBean2 = (ContainedBeanImplementor) beanContainer.getBean(
TheApplicationScopedBean.class,
ContainerManagedLifecycleStrategy.INSTANCE,
FallbackBeanInstanceProducer.INSTANCE
fallbackBeanInstanceProducer
);
dependentBean1 = (ContainedBeanImplementor) beanContainer.getBean(
TheDependentBean.class,
ContainerManagedLifecycleStrategy.INSTANCE,
FallbackBeanInstanceProducer.INSTANCE
fallbackBeanInstanceProducer
);
dependentBean2 = (ContainedBeanImplementor) beanContainer.getBean(
TheDependentBean.class,
ContainerManagedLifecycleStrategy.INSTANCE,
FallbackBeanInstanceProducer.INSTANCE
fallbackBeanInstanceProducer
);
reflectionInstantiatedBean1 = (ContainedBeanImplementor) beanContainer.getBean(
TheReflectionInstantiatedBean.class,
ContainerManagedLifecycleStrategy.INSTANCE,
fallbackBeanInstanceProducer
);
reflectionInstantiatedBean2 = (ContainedBeanImplementor) beanContainer.getBean(
TheReflectionInstantiatedBean.class,
ContainerManagedLifecycleStrategy.INSTANCE,
fallbackBeanInstanceProducer
);
namedApplicationScopedBean1 = (ContainedBeanImplementor) beanContainer.getBean(
TheMainNamedApplicationScopedBeanImpl.NAME,
TheNamedApplicationScopedBean.class,
ContainerManagedLifecycleStrategy.INSTANCE,
FallbackBeanInstanceProducer.INSTANCE
fallbackBeanInstanceProducer
);
namedApplicationScopedBean2 = (ContainedBeanImplementor) beanContainer.getBean(
TheMainNamedApplicationScopedBeanImpl.NAME,
TheNamedApplicationScopedBean.class,
ContainerManagedLifecycleStrategy.INSTANCE,
FallbackBeanInstanceProducer.INSTANCE
fallbackBeanInstanceProducer
);
namedDependentBean1 = (ContainedBeanImplementor) beanContainer.getBean(
TheMainNamedDependentBeanImpl.NAME,
TheNamedDependentBean.class,
ContainerManagedLifecycleStrategy.INSTANCE,
FallbackBeanInstanceProducer.INSTANCE
fallbackBeanInstanceProducer
);
namedDependentBean2 = (ContainedBeanImplementor) beanContainer.getBean(
TheMainNamedDependentBeanImpl.NAME,
TheNamedDependentBean.class,
ContainerManagedLifecycleStrategy.INSTANCE,
FallbackBeanInstanceProducer.INSTANCE
fallbackBeanInstanceProducer
);
namedReflectionInstantiatedBean1 = (ContainedBeanImplementor) beanContainer.getBean(
TheReflectionInstantiatedBean.class.getName(),
TheReflectionInstantiatedBean.class,
ContainerManagedLifecycleStrategy.INSTANCE,
fallbackBeanInstanceProducer
);
namedReflectionInstantiatedBean2 = (ContainedBeanImplementor) beanContainer.getBean(
TheReflectionInstantiatedBean.class.getName(),
TheReflectionInstantiatedBean.class,
ContainerManagedLifecycleStrategy.INSTANCE,
fallbackBeanInstanceProducer
);
}
@ -103,10 +136,14 @@ public class NonRegistryManagedBeanConsumingIntegrator implements Integrator {
applicationScopedBean2.getBeanInstance().ensureInitialized();
dependentBean1.getBeanInstance().ensureInitialized();
dependentBean2.getBeanInstance().ensureInitialized();
reflectionInstantiatedBean1.getBeanInstance().ensureInitialized();
reflectionInstantiatedBean2.getBeanInstance().ensureInitialized();
namedApplicationScopedBean1.getBeanInstance().ensureInitialized();
namedApplicationScopedBean2.getBeanInstance().ensureInitialized();
namedDependentBean1.getBeanInstance().ensureInitialized();
namedDependentBean2.getBeanInstance().ensureInitialized();
namedReflectionInstantiatedBean1.getBeanInstance().ensureInitialized();
namedReflectionInstantiatedBean2.getBeanInstance().ensureInitialized();
}
@Override
@ -115,9 +152,13 @@ public class NonRegistryManagedBeanConsumingIntegrator implements Integrator {
applicationScopedBean2.release();
dependentBean1.release();
dependentBean2.release();
reflectionInstantiatedBean1.release();
reflectionInstantiatedBean2.release();
namedApplicationScopedBean1.release();
namedApplicationScopedBean2.release();
namedDependentBean1.release();
namedDependentBean2.release();
namedReflectionInstantiatedBean1.release();
namedReflectionInstantiatedBean2.release();
}
}

View File

@ -0,0 +1,52 @@
/*
* Hibernate Search, full-text search for your domain model
*
* 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.test.cdi.general.nonregistrymanaged;
import java.util.concurrent.atomic.AtomicInteger;
import org.hibernate.resource.beans.spi.BeanInstanceProducer;
public class TheFallbackBeanInstanceProducer implements BeanInstanceProducer {
private final AtomicInteger instantiationCount = new AtomicInteger( 0 );
private final AtomicInteger namedInstantiationCount = new AtomicInteger( 0 );
@Override
public <B> B produceBeanInstance(Class<B> beanType) {
try {
instantiationCount.getAndIncrement();
return beanType.newInstance();
}
catch (IllegalAccessException|InstantiationException|RuntimeException e) {
throw new AssertionError( "Unexpected error instantiating a bean by type using reflection", e );
}
}
@Override
public <B> B produceBeanInstance(String name, Class<B> beanType) {
try {
Class<?> clazz = getClass().getClassLoader().loadClass( name );
if ( beanType.isAssignableFrom( clazz ) ) {
namedInstantiationCount.getAndIncrement();
return (B) clazz.newInstance();
}
else {
throw new RuntimeException( clazz + " does not extend the contract " + beanType + " as expected" );
}
}
catch (ClassNotFoundException|IllegalAccessException|InstantiationException|RuntimeException e) {
throw new AssertionError( "Unexpected error instantiating a bean by name using reflection", e );
}
}
public int currentInstantiationCount() {
return instantiationCount.get();
}
public int currentNamedInstantiationCount() {
return namedInstantiationCount.get();
}
}

View File

@ -0,0 +1,21 @@
/*
* Hibernate Search, full-text search for your domain model
*
* 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.test.cdi.general.nonregistrymanaged;
import javax.enterprise.inject.Vetoed;
/**
* @author Yoann Rodiere
*/
@Vetoed
public class TheReflectionInstantiatedBean {
public void ensureInitialized() {
// No-op
}
}

View File

@ -29,6 +29,7 @@ import org.hibernate.test.cdi.general.nonregistrymanaged.TheAlternativeNamedDepe
import org.hibernate.test.cdi.general.nonregistrymanaged.TheApplicationScopedBean;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheDependentBean;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheEntity;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheFallbackBeanInstanceProducer;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheMainNamedApplicationScopedBeanImpl;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheMainNamedDependentBeanImpl;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheNamedApplicationScopedBean;
@ -54,8 +55,10 @@ public class NonRegistryManagedDelayedCdiSupportTest extends BaseUnitTestCase {
public void testIt() {
Monitor.reset();
final TheFallbackBeanInstanceProducer fallbackBeanInstanceProducer =
new TheFallbackBeanInstanceProducer();
final NonRegistryManagedBeanConsumingIntegrator beanConsumingIntegrator =
new NonRegistryManagedBeanConsumingIntegrator();
new NonRegistryManagedBeanConsumingIntegrator( fallbackBeanInstanceProducer );
final SeContainerInitializer cdiInitializer = SeContainerInitializer.newInstance()
.disableDiscovery()
@ -83,6 +86,8 @@ public class NonRegistryManagedDelayedCdiSupportTest extends BaseUnitTestCase {
assertEquals( 0, Monitor.theDependentBean().currentInstantiationCount() );
assertEquals( 0, Monitor.theMainNamedDependentBean().currentInstantiationCount() );
assertEquals( 0, Monitor.theAlternativeNamedDependentBean().currentInstantiationCount() );
assertEquals( 0, fallbackBeanInstanceProducer.currentInstantiationCount() );
assertEquals( 0, fallbackBeanInstanceProducer.currentNamedInstantiationCount() );
// Nested dependent bean: 1 instance per bean that depends on it
assertEquals( 1, Monitor.theNestedDependentBean().currentInstantiationCount() );
@ -103,10 +108,14 @@ public class NonRegistryManagedDelayedCdiSupportTest extends BaseUnitTestCase {
assertEquals( 2, Monitor.theMainNamedDependentBean().currentInstantiationCount() );
assertEquals( 0, Monitor.theAlternativeNamedDependentBean().currentInstantiationCount() );
// Reflection-instantiated: 1 instance per bean we requested explicitly
assertEquals( 2, fallbackBeanInstanceProducer.currentInstantiationCount() );
assertEquals( 2, fallbackBeanInstanceProducer.currentNamedInstantiationCount() );
// Nested dependent bean: 1 instance per bean that depends on it
assertEquals( 7, Monitor.theNestedDependentBean().currentInstantiationCount() );
// Expect one PostConstruct call per instance
// Expect one PostConstruct call per CDI bean instance
assertEquals( 1, Monitor.theApplicationScopedBean().currentPostConstructCount() );
assertEquals( 1, Monitor.theMainNamedApplicationScopedBean().currentPostConstructCount() );
assertEquals( 0, Monitor.theAlternativeNamedApplicationScopedBean().currentPostConstructCount() );

View File

@ -31,6 +31,7 @@ import org.hibernate.test.cdi.general.nonregistrymanaged.TheAlternativeNamedDepe
import org.hibernate.test.cdi.general.nonregistrymanaged.TheApplicationScopedBean;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheDependentBean;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheEntity;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheFallbackBeanInstanceProducer;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheMainNamedApplicationScopedBeanImpl;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheMainNamedDependentBeanImpl;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheNamedApplicationScopedBean;
@ -56,8 +57,10 @@ public class NonRegistryManagedExtendedCdiSupportTest extends BaseUnitTestCase {
Monitor.reset();
final ExtendedBeanManagerImpl standIn = new ExtendedBeanManagerImpl();
final TheFallbackBeanInstanceProducer fallbackBeanInstanceProducer =
new TheFallbackBeanInstanceProducer();
final NonRegistryManagedBeanConsumingIntegrator beanConsumingIntegrator =
new NonRegistryManagedBeanConsumingIntegrator();
new NonRegistryManagedBeanConsumingIntegrator( fallbackBeanInstanceProducer );
try (SessionFactoryImplementor sessionFactory = buildSessionFactory( standIn, beanConsumingIntegrator )) {
final SeContainerInitializer cdiInitializer = SeContainerInitializer.newInstance()
@ -86,6 +89,8 @@ public class NonRegistryManagedExtendedCdiSupportTest extends BaseUnitTestCase {
assertEquals( 0, Monitor.theDependentBean().currentInstantiationCount() );
assertEquals( 0, Monitor.theMainNamedDependentBean().currentInstantiationCount() );
assertEquals( 0, Monitor.theAlternativeNamedDependentBean().currentInstantiationCount() );
assertEquals( 0, fallbackBeanInstanceProducer.currentInstantiationCount() );
assertEquals( 0, fallbackBeanInstanceProducer.currentNamedInstantiationCount() );
// Nested dependent bean: 1 instance per bean that depends on it
assertEquals( 1, Monitor.theNestedDependentBean().currentInstantiationCount() );
@ -108,10 +113,14 @@ public class NonRegistryManagedExtendedCdiSupportTest extends BaseUnitTestCase {
assertEquals( 2, Monitor.theMainNamedDependentBean().currentInstantiationCount() );
assertEquals( 0, Monitor.theAlternativeNamedDependentBean().currentInstantiationCount() );
// Reflection-instantiated: 1 instance per bean we requested explicitly
assertEquals( 2, fallbackBeanInstanceProducer.currentInstantiationCount() );
assertEquals( 2, fallbackBeanInstanceProducer.currentNamedInstantiationCount() );
// Nested dependent bean: 1 instance per bean that depends on it
assertEquals( 7, Monitor.theNestedDependentBean().currentInstantiationCount() );
// Expect one PostConstruct call per instance
// Expect one PostConstruct call per CDI bean instance
assertEquals( 1, Monitor.theApplicationScopedBean().currentPostConstructCount() );
assertEquals( 1, Monitor.theMainNamedApplicationScopedBean().currentPostConstructCount() );
assertEquals( 0, Monitor.theAlternativeNamedApplicationScopedBean().currentPostConstructCount() );
@ -132,8 +141,8 @@ public class NonRegistryManagedExtendedCdiSupportTest extends BaseUnitTestCase {
assertEquals( 0, Monitor.theNestedDependentBean().currentPreDestroyCount() );
}
// After the CDI context has ended, PreDestroy should have been called on every "normal-scoped" bean
// (i.e. all beans excepting the dependent ones we requested explicitly and haven't released yet)
// After the CDI context has ended, PreDestroy should have been called on every "normal-scoped" CDI bean
// (i.e. all CDI beans excepting the dependent ones we requested explicitly and haven't released yet)
assertEquals( 1, Monitor.theApplicationScopedBean().currentPreDestroyCount() );
assertEquals( 1, Monitor.theMainNamedApplicationScopedBean().currentPreDestroyCount() );
assertEquals( 0, Monitor.theAlternativeNamedApplicationScopedBean().currentPreDestroyCount() );

View File

@ -29,6 +29,7 @@ import org.hibernate.test.cdi.general.nonregistrymanaged.TheAlternativeNamedDepe
import org.hibernate.test.cdi.general.nonregistrymanaged.TheApplicationScopedBean;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheDependentBean;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheEntity;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheFallbackBeanInstanceProducer;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheMainNamedApplicationScopedBeanImpl;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheMainNamedDependentBeanImpl;
import org.hibernate.test.cdi.general.nonregistrymanaged.TheNamedApplicationScopedBean;
@ -53,8 +54,10 @@ public class NonRegistryManagedStandardCdiSupportTest extends BaseUnitTestCase {
public void testIt() {
Monitor.reset();
final TheFallbackBeanInstanceProducer fallbackBeanInstanceProducer =
new TheFallbackBeanInstanceProducer();
final NonRegistryManagedBeanConsumingIntegrator beanConsumingIntegrator =
new NonRegistryManagedBeanConsumingIntegrator();
new NonRegistryManagedBeanConsumingIntegrator( fallbackBeanInstanceProducer );
final SeContainerInitializer cdiInitializer = SeContainerInitializer.newInstance()
.disableDiscovery()
@ -82,6 +85,8 @@ public class NonRegistryManagedStandardCdiSupportTest extends BaseUnitTestCase {
assertEquals( 0, Monitor.theDependentBean().currentInstantiationCount() );
assertEquals( 0, Monitor.theMainNamedDependentBean().currentInstantiationCount() );
assertEquals( 0, Monitor.theAlternativeNamedDependentBean().currentInstantiationCount() );
assertEquals( 0, fallbackBeanInstanceProducer.currentInstantiationCount() );
assertEquals( 0, fallbackBeanInstanceProducer.currentNamedInstantiationCount() );
// Nested dependent bean: 1 instance per bean that depends on it
assertEquals( 1, Monitor.theNestedDependentBean().currentInstantiationCount() );
@ -102,10 +107,14 @@ public class NonRegistryManagedStandardCdiSupportTest extends BaseUnitTestCase {
assertEquals( 2, Monitor.theMainNamedDependentBean().currentInstantiationCount() );
assertEquals( 0, Monitor.theAlternativeNamedDependentBean().currentInstantiationCount() );
// Reflection-instantiated: 1 instance per bean we requested explicitly
assertEquals( 2, fallbackBeanInstanceProducer.currentInstantiationCount() );
assertEquals( 2, fallbackBeanInstanceProducer.currentNamedInstantiationCount() );
// Nested dependent bean: 1 instance per bean that depends on it
assertEquals( 7, Monitor.theNestedDependentBean().currentInstantiationCount() );
// Expect one PostConstruct call per instance
// Expect one PostConstruct call per CDI bean instance
assertEquals( 1, Monitor.theApplicationScopedBean().currentPostConstructCount() );
assertEquals( 1, Monitor.theMainNamedApplicationScopedBean().currentPostConstructCount() );
assertEquals( 0, Monitor.theAlternativeNamedApplicationScopedBean().currentPostConstructCount() );
@ -145,7 +154,7 @@ public class NonRegistryManagedStandardCdiSupportTest extends BaseUnitTestCase {
assertEquals( 6, Monitor.theNestedDependentBean().currentPreDestroyCount() );
}
// After the CDI context has ended, PreDestroy should have been called on every created bean
// After the CDI context has ended, PreDestroy should have been called on every created CDI bean
// (see the assertions about instantiations above for an explanation of the expected counts)
assertEquals( 1, Monitor.theApplicationScopedBean().currentPreDestroyCount() );
assertEquals( 1, Monitor.theMainNamedApplicationScopedBean().currentPreDestroyCount() );