From c23d450b5227359a7188ef44f7ff84c3cd93af86 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Thu, 12 Sep 2024 15:54:57 +0200 Subject: [PATCH] ArC: fix the situation when a framework bean uses an application decorator ArC-generated classes for framework beans are by default _not_ application classes. This causes problems in hierarchical classloader environments (dev/test mode) when there is an application decorator that applies to the framework bean. This commit fixes that issue by turning the ArC-generated classes for framework beans into application classes whenever an application decorator applies. This makes package access impossible, which is an unfortunate downside. The problem doesn't exist in a flat classloading environment, such as prod mode. (cherry picked from commit f9b21643d53a3ac785b2a05fb46ab6fb8619be74) --- .../DecoratorOfExternalBeanTest.java | 49 +++++++++++++++++++ extensions/arc/test-supplement/pom.xml | 7 +++ .../supplement/SomeBeanInExternalLibrary.java | 11 +++++ .../SomeInterfaceInExternalLibrary.java | 5 ++ .../src/main/resources/META-INF/beans.xml | 0 .../quarkus/arc/processor/BeanGenerator.java | 4 +- .../io/quarkus/arc/processor/BeanInfo.java | 10 ++++ .../arc/processor/ClientProxyGenerator.java | 5 +- .../arc/processor/SubclassGenerator.java | 4 +- 9 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 extensions/arc/deployment/src/test/java/io/quarkus/arc/test/decorator/DecoratorOfExternalBeanTest.java create mode 100644 extensions/arc/test-supplement/src/main/java/io/quarkus/arc/test/supplement/SomeBeanInExternalLibrary.java create mode 100644 extensions/arc/test-supplement/src/main/java/io/quarkus/arc/test/supplement/SomeInterfaceInExternalLibrary.java create mode 100644 extensions/arc/test-supplement/src/main/resources/META-INF/beans.xml diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/decorator/DecoratorOfExternalBeanTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/decorator/DecoratorOfExternalBeanTest.java new file mode 100644 index 0000000000000..8a166fb63c248 --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/decorator/DecoratorOfExternalBeanTest.java @@ -0,0 +1,49 @@ +package io.quarkus.arc.test.decorator; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; + +import jakarta.decorator.Decorator; +import jakarta.decorator.Delegate; +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.test.supplement.SomeBeanInExternalLibrary; +import io.quarkus.arc.test.supplement.SomeInterfaceInExternalLibrary; +import io.quarkus.builder.Version; +import io.quarkus.maven.dependency.Dependency; +import io.quarkus.test.QuarkusUnitTest; + +public class DecoratorOfExternalBeanTest { + // the test includes an _application_ decorator (in the Runtime CL) that applies + // to a bean that is _outside_ of the application (in the Base Runtime CL) + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar.addClass(MyDecorator.class)) + // we need a non-application archive, so cannot use `withAdditionalDependency()` + .setForcedDependencies(List.of(Dependency.of("io.quarkus", "quarkus-arc-test-supplement", Version.getVersion()))); + + @Inject + SomeBeanInExternalLibrary bean; + + @Test + public void test() { + assertEquals("Delegated: Hello", bean.hello()); + } + + @Decorator + public static class MyDecorator implements SomeInterfaceInExternalLibrary { + @Inject + @Delegate + SomeInterfaceInExternalLibrary delegate; + + @Override + public String hello() { + return "Delegated: " + delegate.hello(); + } + } +} diff --git a/extensions/arc/test-supplement/pom.xml b/extensions/arc/test-supplement/pom.xml index 0f40b77893883..4f9e04eb3b135 100644 --- a/extensions/arc/test-supplement/pom.xml +++ b/extensions/arc/test-supplement/pom.xml @@ -13,4 +13,11 @@ Quarkus - ArC - Test Supplement Supplement archive for ArC tests + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + diff --git a/extensions/arc/test-supplement/src/main/java/io/quarkus/arc/test/supplement/SomeBeanInExternalLibrary.java b/extensions/arc/test-supplement/src/main/java/io/quarkus/arc/test/supplement/SomeBeanInExternalLibrary.java new file mode 100644 index 0000000000000..1e722755fa73c --- /dev/null +++ b/extensions/arc/test-supplement/src/main/java/io/quarkus/arc/test/supplement/SomeBeanInExternalLibrary.java @@ -0,0 +1,11 @@ +package io.quarkus.arc.test.supplement; + +import jakarta.enterprise.context.Dependent; + +@Dependent +public class SomeBeanInExternalLibrary implements SomeInterfaceInExternalLibrary { + @Override + public String hello() { + return "Hello"; + } +} diff --git a/extensions/arc/test-supplement/src/main/java/io/quarkus/arc/test/supplement/SomeInterfaceInExternalLibrary.java b/extensions/arc/test-supplement/src/main/java/io/quarkus/arc/test/supplement/SomeInterfaceInExternalLibrary.java new file mode 100644 index 0000000000000..f621a3c9d27a0 --- /dev/null +++ b/extensions/arc/test-supplement/src/main/java/io/quarkus/arc/test/supplement/SomeInterfaceInExternalLibrary.java @@ -0,0 +1,5 @@ +package io.quarkus.arc.test.supplement; + +public interface SomeInterfaceInExternalLibrary { + String hello(); +} diff --git a/extensions/arc/test-supplement/src/main/resources/META-INF/beans.xml b/extensions/arc/test-supplement/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java index da197f726362b..7b763b4f47fb3 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java @@ -344,7 +344,9 @@ Collection generateClassBean(BeanInfo bean, ClassInfo beanClass) { return Collections.emptyList(); } - boolean isApplicationClass = applicationClassPredicate.test(beanClass.name()) || bean.isForceApplicationClass(); + boolean isApplicationClass = applicationClassPredicate.test(beanClass.name()) + || bean.isForceApplicationClass() + || bean.hasBoundDecoratorWhichIsApplicationClass(applicationClassPredicate); ResourceClassOutput classOutput = new ResourceClassOutput(isApplicationClass, name -> name.equals(generatedName) ? SpecialType.BEAN : null, generateSources); diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java index 8b3d1ceca018d..89e964f9cab17 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java @@ -18,6 +18,7 @@ import java.util.OptionalInt; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.stream.Collectors; import jakarta.enterprise.inject.Typed; @@ -514,6 +515,15 @@ public List getBoundDecorators() { return bound; } + boolean hasBoundDecoratorWhichIsApplicationClass(Predicate isApplicationClass) { + for (DecoratorInfo decorator : getBoundDecorators()) { + if (isApplicationClass.test(decorator.getImplClazz().name())) { + return true; + } + } + return false; + } + /** * * @return the list of around invoke interceptor methods declared in the hierarchy of a bean class 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 b7c262df9af8b..f5c64a88e4691 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 @@ -98,8 +98,9 @@ Collection generate(BeanInfo bean, String beanClassName, return Collections.emptyList(); } - boolean applicationClass = applicationClassPredicate.test(getApplicationClassTestName(bean)); - ResourceClassOutput classOutput = new ResourceClassOutput(applicationClass, + boolean isApplicationClass = applicationClassPredicate.test(getApplicationClassTestName(bean)) + || bean.hasBoundDecoratorWhichIsApplicationClass(applicationClassPredicate); + ResourceClassOutput classOutput = new ResourceClassOutput(isApplicationClass, name -> name.equals(generatedName) ? SpecialType.CLIENT_PROXY : null, generateSources); // Foo_ClientProxy extends Foo implements ClientProxy diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java index b14ee140c82b4..23ad18173f797 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java @@ -112,7 +112,9 @@ Collection generate(BeanInfo bean, String beanClassName) { return Collections.emptyList(); } - ResourceClassOutput classOutput = new ResourceClassOutput(applicationClassPredicate.test(bean.getBeanClass()), + boolean isApplicationClass = applicationClassPredicate.test(bean.getBeanClass()) + || bean.hasBoundDecoratorWhichIsApplicationClass(applicationClassPredicate); + ResourceClassOutput classOutput = new ResourceClassOutput(isApplicationClass, name -> name.equals(generatedName) ? SpecialType.SUBCLASS : null, generateSources);