From 2f95160996a08b6a0e05700d710918220959fe96 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Thu, 27 Apr 2023 10:25:38 +0200 Subject: [PATCH] ArC - fix CreationalContext handling when used directly with BeanManager Co-authored-by: Ladicek --- .../io/quarkus/arc/impl/ArcContainerImpl.java | 15 ++- .../io/quarkus/arc/impl/BeanManagerImpl.java | 5 +- .../src/test/resources/testng.xml | 2 + ...DependentPreDestroyOnlyCalledOnceTest.java | 123 ++++++++++++++++++ .../arc/test/beanmanager/BeanManagerTest.java | 3 +- 5 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/bean/destroy/DependentPreDestroyOnlyCalledOnceTest.java diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java index 8d3eefbdf3988..2f6a85ecd8aa3 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java @@ -471,12 +471,21 @@ private InstanceHandle instanceHandle(Type type, Annotation... qualifiers static InstanceHandle beanInstanceHandle(InjectableBean bean, CreationalContextImpl parentContext, boolean resetCurrentInjectionPoint, Consumer destroyLogic) { + return beanInstanceHandle(bean, parentContext, resetCurrentInjectionPoint, destroyLogic, false); + } + + static InstanceHandle beanInstanceHandle(InjectableBean bean, CreationalContextImpl parentContext, + boolean resetCurrentInjectionPoint, Consumer destroyLogic, boolean useParentCreationalContextDirectly) { if (bean != null) { if (parentContext == null && Dependent.class.equals(bean.getScope())) { parentContext = new CreationalContextImpl<>(null); } - CreationalContextImpl creationalContext = parentContext != null ? parentContext.child(bean) - : new CreationalContextImpl<>(bean); + CreationalContextImpl creationalContext; + if (parentContext != null) { + creationalContext = useParentCreationalContextDirectly ? parentContext : parentContext.child(bean); + } else { + creationalContext = new CreationalContextImpl<>(bean); + } InjectionPoint prev = null; if (resetCurrentInjectionPoint) { prev = InjectionPointProvider.set(CurrentInjectionPointProvider.EMPTY); @@ -494,7 +503,7 @@ static InstanceHandle beanInstanceHandle(InjectableBean bean, Creation } } - InstanceHandle beanInstanceHandle(InjectableBean bean, CreationalContextImpl parentContext) { + static InstanceHandle beanInstanceHandle(InjectableBean bean, CreationalContextImpl parentContext) { return beanInstanceHandle(bean, parentContext, true, null); } diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanManagerImpl.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanManagerImpl.java index 141cb5a9753f6..2386de2352730 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanManagerImpl.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanManagerImpl.java @@ -60,7 +60,8 @@ public Object getReference(Bean bean, Type beanType, CreationalContext ctx + "; its bean types are: " + bean.getTypes()); } if (bean instanceof InjectableBean && ctx instanceof CreationalContextImpl) { - return ArcContainerImpl.instance().beanInstanceHandle((InjectableBean) bean, (CreationalContextImpl) ctx).get(); + return ArcContainerImpl.beanInstanceHandle((InjectableBean) bean, (CreationalContextImpl) ctx, true, null, true) + .get(); } throw new IllegalArgumentException( "Arguments must be instances of " + InjectableBean.class + " and " + CreationalContextImpl.class + ": \nbean: " @@ -80,7 +81,7 @@ public Object getInjectableReference(InjectionPoint ij, CreationalContext ctx InjectableBean bean = (InjectableBean) resolve(beans); InjectionPoint prev = InjectionPointProvider.set(ij); try { - return ArcContainerImpl.beanInstanceHandle(bean, (CreationalContextImpl) ctx, false, null).get(); + return ArcContainerImpl.beanInstanceHandle(bean, (CreationalContextImpl) ctx, false, null, true).get(); } finally { InjectionPointProvider.set(prev); } diff --git a/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml b/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml index c27324111b129..46db3531d29f0 100644 --- a/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml +++ b/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml @@ -383,6 +383,8 @@ + + diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/bean/destroy/DependentPreDestroyOnlyCalledOnceTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/bean/destroy/DependentPreDestroyOnlyCalledOnceTest.java new file mode 100644 index 0000000000000..718fb6ec67f4c --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/bean/destroy/DependentPreDestroyOnlyCalledOnceTest.java @@ -0,0 +1,123 @@ +package io.quarkus.arc.test.bean.destroy; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.PreDestroy; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.context.spi.CreationalContext; +import jakarta.enterprise.inject.spi.Bean; +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.inject.Inject; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.test.ArcTestContainer; + +public class DependentPreDestroyOnlyCalledOnceTest { + @RegisterExtension + ArcTestContainer container = new ArcTestContainer(MyDependency.class, MyBean.class, MyInterceptedBean.class, + MyInterceptorBinding.class, MyInterceptor.class); + + @BeforeEach + public void setUp() { + MyDependency.preDestroy = 0; + MyBean.preDestroy = 0; + MyInterceptedBean.preDestroy = 0; + } + + @SuppressWarnings("unchecked") + @Test + public void preDestroyOnBeanOnly() { + BeanManager beanManager = Arc.container().beanManager(); + Bean bean = (Bean) beanManager.resolve(beanManager.getBeans(MyBean.class)); + CreationalContext ctx = beanManager.createCreationalContext(bean); + MyBean instance = (MyBean) beanManager.getReference(bean, MyBean.class, ctx); + bean.destroy(instance, ctx); + + assertEquals(1, MyDependency.preDestroy); + assertEquals(1, MyBean.preDestroy); + } + + @SuppressWarnings("unchecked") + @Test + public void preDestroyOnBeanAndInterceptor() { + BeanManager beanManager = Arc.container().beanManager(); + Bean bean = (Bean) beanManager.resolve( + beanManager.getBeans(MyInterceptedBean.class)); + CreationalContext ctx = beanManager.createCreationalContext(bean); + MyInterceptedBean instance = (MyInterceptedBean) beanManager.getReference(bean, MyInterceptedBean.class, ctx); + bean.destroy(instance, ctx); + + assertEquals(1, MyDependency.preDestroy); + assertEquals(1, MyInterceptedBean.preDestroy); + assertEquals(1, MyInterceptor.preDestroy); + } + + @Dependent + static class MyDependency { + static int preDestroy = 0; + + @PreDestroy + void destroy() { + preDestroy++; + } + } + + @Dependent + static class MyBean { + static int preDestroy = 0; + + @Inject + MyDependency dependency; + + @PreDestroy + void destroy() { + preDestroy++; + } + } + + @Dependent + @MyInterceptorBinding + static class MyInterceptedBean { + static int preDestroy = 0; + + @Inject + MyDependency dependency; + + @PreDestroy + void destroy() { + preDestroy++; + } + } + + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @InterceptorBinding + @interface MyInterceptorBinding { + } + + @MyInterceptorBinding + @Interceptor + @Priority(1) + static class MyInterceptor { + static int preDestroy = 0; + + @PreDestroy + void preDestroy(InvocationContext ctx) throws Exception { + preDestroy++; + ctx.proceed(); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/beanmanager/BeanManagerTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/beanmanager/BeanManagerTest.java index 776191168bdc2..99536e2a7a078 100644 --- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/beanmanager/BeanManagerTest.java +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/beanmanager/BeanManagerTest.java @@ -125,7 +125,7 @@ public void testGetReference() { }); Legacy legacy = (Legacy) beanManager.getReference(legacyBean, Legacy.class, ctx); assertNotNull(legacy.getBeanManager()); - ctx.release(); + legacyBean.destroy((AlternativeLegacy) legacy, ctx); assertTrue(Legacy.DESTROYED.get()); } @@ -179,7 +179,6 @@ public Annotated getAnnotated() { assertNull(injectableReference.injectionPoint.getBean()); } - @SuppressWarnings("serial") @Test public void testResolveInterceptors() { BeanManager beanManager = Arc.container().beanManager();