From 3390d0362a81b95f034a5ed8a18b856d729fc63a Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 8 Aug 2022 13:35:02 +0300 Subject: [PATCH] Ensure that Quarkus can mock final methods of beans created from producers Fixes: #26733 --- .../RequestScopedFinalMethodsTest.java | 69 ++++++++----------- .../arc/processor/ClientProxyGenerator.java | 37 ++++++---- 2 files changed, 54 insertions(+), 52 deletions(-) diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/unproxyable/RequestScopedFinalMethodsTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/unproxyable/RequestScopedFinalMethodsTest.java index 212addc838fa1..5c21aedd73fe3 100644 --- a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/unproxyable/RequestScopedFinalMethodsTest.java +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/unproxyable/RequestScopedFinalMethodsTest.java @@ -1,15 +1,7 @@ package io.quarkus.arc.test.unproxyable; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -import javax.annotation.PostConstruct; import javax.enterprise.context.Dependent; import javax.enterprise.context.RequestScoped; -import javax.enterprise.event.Event; -import javax.enterprise.event.Observes; -import javax.inject.Inject; -import javax.inject.Singleton; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -19,6 +11,7 @@ import io.quarkus.arc.ArcContainer; import io.quarkus.arc.InstanceHandle; import io.quarkus.arc.ManagedContext; +import io.quarkus.arc.Unremovable; import io.quarkus.test.QuarkusUnitTest; public class RequestScopedFinalMethodsTest { @@ -26,7 +19,7 @@ public class RequestScopedFinalMethodsTest { @RegisterExtension public static QuarkusUnitTest container = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar - .addClasses(RequestScopedBean.class)); + .addClasses(RequestScopedBean.class, OtherRequestScopedBean.class, OtherRequestScopeBeanProducer.class)); @Test public void testRequestScopedBeanWorksProperly() { @@ -36,22 +29,35 @@ public void testRequestScopedBeanWorksProperly() { InstanceHandle handle = container.instance(RequestScopedBean.class); Assertions.assertTrue(handle.isAvailable()); + InstanceHandle otherHandle = container.instance(OtherRequestScopedBean.class); + Assertions.assertTrue(otherHandle.isAvailable()); RequestScopedBean bean = handle.get(); Assertions.assertNull(bean.getProp()); bean.setProp(100); Assertions.assertEquals(100, bean.getProp()); + OtherRequestScopedBean otherBean = otherHandle.get(); + Assertions.assertNull(otherBean.getProp()); + otherBean.setProp(100); + Assertions.assertEquals(100, otherBean.getProp()); + requestContext.terminate(); requestContext.activate(); handle = container.instance(RequestScopedBean.class); - bean = handle.get(); Assertions.assertTrue(handle.isAvailable()); + bean = handle.get(); Assertions.assertNull(bean.getProp()); + + otherHandle = container.instance(OtherRequestScopedBean.class); + Assertions.assertTrue(otherHandle.isAvailable()); + otherBean = otherHandle.get(); + Assertions.assertNull(otherBean.getProp()); } @RequestScoped + @Unremovable static class RequestScopedBean { private Integer prop = null; @@ -64,41 +70,26 @@ public final void setProp(Integer prop) { } } - @Dependent - static class StringProducer { - - @Inject - Event event; + static class OtherRequestScopedBean { + private Integer prop = null; - void fire(String value) { - event.fire(value); + public final Integer getProp() { + return prop; } - } - - @Singleton - static class StringObserver { - - private List events; - - @Inject - RequestScopedBean requestScopedBean; - - @PostConstruct - void init() { - events = new CopyOnWriteArrayList<>(); + public final void setProp(Integer prop) { + this.prop = prop; } + } - void observeSync(@Observes Integer value) { - Integer oldValue = requestScopedBean.getProp(); - Integer newValue = oldValue == null ? value : value + oldValue; - requestScopedBean.setProp(newValue); - events.add(newValue); - } + @Dependent + static class OtherRequestScopeBeanProducer { - List getEvents() { - return events; + @RequestScoped + @Unremovable + public OtherRequestScopedBean produce() { + return new OtherRequestScopedBean(); } - } + } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java index 6a9ac596e62dd..a773690f517c8 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java @@ -325,25 +325,20 @@ Collection getDelegatingMethods(BeanInfo bean, Consumer> methodsFromWhichToRemoveFinal = new HashMap<>(); ClassInfo classInfo = bean.getTarget().get().asClass(); - Methods.addDelegatingMethods(index, classInfo, - methods, methodsFromWhichToRemoveFinal, transformUnproxyableClasses); - if (!methodsFromWhichToRemoveFinal.isEmpty()) { - for (Map.Entry> entry : methodsFromWhichToRemoveFinal.entrySet()) { - String className = entry.getKey(); - bytecodeTransformerConsumer.accept(new BytecodeTransformer(className, - new Methods.RemoveFinalFromMethod(className, entry.getValue()))); - } - } + addDelegatesAndTrasformIfNecessary(bytecodeTransformerConsumer, transformUnproxyableClasses, methods, index, + methodsFromWhichToRemoveFinal, classInfo); } else if (bean.isProducerMethod()) { + Map> methodsFromWhichToRemoveFinal = new HashMap<>(); MethodInfo producerMethod = bean.getTarget().get().asMethod(); ClassInfo returnTypeClass = getClassByName(index, producerMethod.returnType()); - Methods.addDelegatingMethods(index, returnTypeClass, methods, null, - transformUnproxyableClasses); + addDelegatesAndTrasformIfNecessary(bytecodeTransformerConsumer, transformUnproxyableClasses, methods, index, + methodsFromWhichToRemoveFinal, returnTypeClass); } else if (bean.isProducerField()) { + Map> methodsFromWhichToRemoveFinal = new HashMap<>(); FieldInfo producerField = bean.getTarget().get().asField(); ClassInfo fieldClass = getClassByName(index, producerField.type()); - Methods.addDelegatingMethods(index, fieldClass, methods, null, - transformUnproxyableClasses); + addDelegatesAndTrasformIfNecessary(bytecodeTransformerConsumer, transformUnproxyableClasses, methods, index, + methodsFromWhichToRemoveFinal, fieldClass); } else if (bean.isSynthetic()) { Methods.addDelegatingMethods(index, bean.getImplClazz(), methods, null, transformUnproxyableClasses); @@ -352,6 +347,22 @@ Collection getDelegatingMethods(BeanInfo bean, Consumer bytecodeTransformerConsumer, + boolean transformUnproxyableClasses, + Map methods, IndexView index, + Map> methodsFromWhichToRemoveFinal, + ClassInfo fieldClass) { + Methods.addDelegatingMethods(index, fieldClass, methods, methodsFromWhichToRemoveFinal, + transformUnproxyableClasses); + if (!methodsFromWhichToRemoveFinal.isEmpty()) { + for (Map.Entry> entry : methodsFromWhichToRemoveFinal.entrySet()) { + String className = entry.getKey(); + bytecodeTransformerConsumer.accept(new BytecodeTransformer(className, + new Methods.RemoveFinalFromMethod(className, entry.getValue()))); + } + } + } + private DotName getApplicationClassTestName(BeanInfo bean) { DotName testedName; // For producers we need to test the produced type