Skip to content

Commit

Permalink
ArC: fix the situation when a framework bean uses an application deco…
Browse files Browse the repository at this point in the history
…rator

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 f9b2164)
  • Loading branch information
Ladicek authored and gsmet committed Nov 13, 2024
1 parent 0073e91 commit c23d450
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
7 changes: 7 additions & 0 deletions extensions/arc/test-supplement/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,11 @@
<name>Quarkus - ArC - Test Supplement</name>
<description>Supplement archive for ArC tests</description>

<dependencies>
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -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";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.quarkus.arc.test.supplement;

public interface SomeInterfaceInExternalLibrary {
String hello();
}
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,9 @@ Collection<Resource> 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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -514,6 +515,15 @@ public List<DecoratorInfo> getBoundDecorators() {
return bound;
}

boolean hasBoundDecoratorWhichIsApplicationClass(Predicate<DotName> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ Collection<Resource> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ Collection<Resource> 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);

Expand Down

0 comments on commit c23d450

Please sign in to comment.