Skip to content

Commit 6ed9ec4

Browse files
authored
Merge pull request #47421 from gsmet/fix-47003
Also register superclasses as bean types in ResteasyReactiveProcessor
2 parents 9af05b1 + 458a360 commit 6ed9ec4

File tree

3 files changed

+153
-18
lines changed

3 files changed

+153
-18
lines changed

extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java

+17-15
Original file line numberDiff line numberDiff line change
@@ -1049,21 +1049,21 @@ private boolean isRecord(IndexView index, DotName name) {
10491049

10501050
private AnnotationInstance createTypedAnnotationInstance(ClassInfo clazz,
10511051
BeanArchiveIndexBuildItem beanArchiveIndexBuildItem) {
1052-
Set<DotName> interfaceNames = new HashSet<>();
1052+
Set<DotName> allBeanTypes = new HashSet<>();
1053+
allBeanTypes.add(clazz.name());
1054+
10531055
ClassInfo currentClazz = clazz;
1054-
while (!ResteasyReactiveDotNames.OBJECT.equals(currentClazz.name())) {
1055-
currentClazz.interfaceNames().forEach(iface -> interfaceNames.add(iface));
1056-
// inspect super class
1056+
while (!ResteasyReactiveDotNames.OBJECT.equals(currentClazz.name()) && currentClazz != null) {
1057+
if (currentClazz.isAbstract()) {
1058+
allBeanTypes.add(currentClazz.name());
1059+
}
1060+
allBeanTypes.addAll(getAllParentInterfaces(currentClazz.interfaceNames(), beanArchiveIndexBuildItem));
10571061
currentClazz = beanArchiveIndexBuildItem.getIndex().getClassByName(currentClazz.superName());
10581062
}
1059-
Set<DotName> allInterfaces = new HashSet<>();
1060-
recursiveInterfaceSearch(interfaceNames, allInterfaces, beanArchiveIndexBuildItem);
1061-
AnnotationValue[] annotationValues = new AnnotationValue[allInterfaces.size() + 1];
1062-
// always add the bean impl class
1063-
annotationValues[0] = AnnotationValue.createClassValue("value",
1064-
Type.create(clazz.name(), Type.Kind.CLASS));
1065-
Iterator<DotName> iterator = allInterfaces.iterator();
1066-
for (int i = 1; i < annotationValues.length; i++) {
1063+
1064+
AnnotationValue[] annotationValues = new AnnotationValue[allBeanTypes.size()];
1065+
Iterator<DotName> iterator = allBeanTypes.iterator();
1066+
for (int i = 0; i < annotationValues.length; i++) {
10671067
annotationValues[i] = AnnotationValue.createClassValue("value",
10681068
Type.create(iterator.next(), Type.Kind.CLASS));
10691069
}
@@ -1072,9 +1072,9 @@ private AnnotationInstance createTypedAnnotationInstance(ClassInfo clazz,
10721072
annotationValues) });
10731073
}
10741074

1075-
private void recursiveInterfaceSearch(Set<DotName> interfacesToProcess, Set<DotName> allDiscoveredInterfaces,
1075+
private Set<DotName> getAllParentInterfaces(Collection<DotName> interfacesToProcess,
10761076
BeanArchiveIndexBuildItem beanArchiveIndexBuildItem) {
1077-
allDiscoveredInterfaces.addAll(interfacesToProcess);
1077+
Set<DotName> allDiscoveredInterfaces = new HashSet<DotName>(interfacesToProcess);
10781078
Set<DotName> additionalInterfacesToProcess = new HashSet<>();
10791079
for (DotName name : interfacesToProcess) {
10801080
ClassInfo clazz = beanArchiveIndexBuildItem.getIndex().getClassByName(name);
@@ -1085,8 +1085,10 @@ private void recursiveInterfaceSearch(Set<DotName> interfacesToProcess, Set<DotN
10851085
}
10861086
if (!additionalInterfacesToProcess.isEmpty()) {
10871087
// recursively process newly found interfaces
1088-
recursiveInterfaceSearch(additionalInterfacesToProcess, allDiscoveredInterfaces, beanArchiveIndexBuildItem);
1088+
allDiscoveredInterfaces.addAll(getAllParentInterfaces(additionalInterfacesToProcess, beanArchiveIndexBuildItem));
10891089
}
1090+
1091+
return allDiscoveredInterfaces;
10901092
}
10911093

10921094
private Collection<DotName> additionalContextTypes(List<ContextTypeBuildItem> contextTypeBuildItems) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package io.quarkus.resteasy.reactive.server.test.resource.basic;
2+
3+
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.equalTo;
5+
6+
import java.util.function.Supplier;
7+
8+
import jakarta.enterprise.context.ApplicationScoped;
9+
import jakarta.enterprise.inject.Instance;
10+
import jakarta.enterprise.inject.spi.CDI;
11+
import jakarta.ws.rs.core.Context;
12+
import jakarta.ws.rs.core.HttpHeaders;
13+
14+
import org.jboss.shrinkwrap.api.ShrinkWrap;
15+
import org.jboss.shrinkwrap.api.spec.JavaArchive;
16+
import org.junit.jupiter.api.Test;
17+
import org.junit.jupiter.api.extension.RegisterExtension;
18+
19+
import io.quarkus.arc.Unremovable;
20+
import io.quarkus.test.QuarkusUnitTest;
21+
22+
public class ContextAndInstanceTest {
23+
24+
@RegisterExtension
25+
static QuarkusUnitTest testExtension = new QuarkusUnitTest()
26+
.setArchiveProducer(new Supplier<>() {
27+
@Override
28+
public JavaArchive get() {
29+
JavaArchive jar = ShrinkWrap.create(JavaArchive.class);
30+
jar.addClasses(SummaryGeneratorInterface.class);
31+
jar.addClasses(SummaryGeneratorSubInterface.class);
32+
jar.addClasses(SummaryGenerator.class);
33+
jar.addClasses(GermanSummaryGenerator.class);
34+
jar.addClasses(GreetingGeneratorInterface.class);
35+
jar.addClasses(GreetingGeneratorSubInterface.class);
36+
jar.addClasses(GermanGreetingGenerator.class);
37+
return jar;
38+
}
39+
});
40+
41+
@Test
42+
void testContextWithAbstractClass() {
43+
{
44+
Instance<GermanSummaryGenerator> greetingGenerators = CDI.current()
45+
.select(ContextAndInstanceTest.GermanSummaryGenerator.class);
46+
assertThat(greetingGenerators.isResolvable(), equalTo(true));
47+
}
48+
49+
{
50+
Instance<SummaryGenerator> greetingGenerators = CDI.current()
51+
.select(ContextAndInstanceTest.SummaryGenerator.class);
52+
assertThat(greetingGenerators.isResolvable(), equalTo(true));
53+
}
54+
55+
{
56+
Instance<SummaryGeneratorInterface> greetingGenerators = CDI.current()
57+
.select(ContextAndInstanceTest.SummaryGeneratorInterface.class);
58+
assertThat(greetingGenerators.isResolvable(), equalTo(true));
59+
}
60+
61+
{
62+
Instance<SummaryGeneratorSubInterface> greetingGenerators = CDI.current()
63+
.select(ContextAndInstanceTest.SummaryGeneratorSubInterface.class);
64+
assertThat(greetingGenerators.isResolvable(), equalTo(true));
65+
}
66+
}
67+
68+
@Test
69+
void testContextWithOnlyInterfaces() {
70+
{
71+
Instance<GreetingGeneratorInterface> greetingGenerators = CDI.current()
72+
.select(ContextAndInstanceTest.GreetingGeneratorInterface.class);
73+
assertThat(greetingGenerators.isResolvable(), equalTo(true));
74+
}
75+
76+
{
77+
Instance<GreetingGeneratorSubInterface> greetingGenerators = CDI.current()
78+
.select(ContextAndInstanceTest.GreetingGeneratorSubInterface.class);
79+
assertThat(greetingGenerators.isResolvable(), equalTo(true));
80+
}
81+
82+
{
83+
Instance<AnotherSubInterface> greetingGenerators = CDI.current()
84+
.select(ContextAndInstanceTest.AnotherSubInterface.class);
85+
assertThat(greetingGenerators.isResolvable(), equalTo(true));
86+
}
87+
88+
{
89+
Instance<AnotherInterface> greetingGenerators = CDI.current()
90+
.select(ContextAndInstanceTest.AnotherInterface.class);
91+
assertThat(greetingGenerators.isResolvable(), equalTo(true));
92+
}
93+
}
94+
95+
public interface SummaryGeneratorInterface {
96+
}
97+
98+
public interface SummaryGeneratorSubInterface extends SummaryGeneratorInterface {
99+
}
100+
101+
public abstract static class SummaryGenerator implements SummaryGeneratorSubInterface {
102+
}
103+
104+
@Unremovable
105+
@ApplicationScoped
106+
public static class GermanSummaryGenerator extends SummaryGenerator {
107+
108+
@Context
109+
HttpHeaders headers;
110+
}
111+
112+
public interface GreetingGeneratorInterface {
113+
}
114+
115+
public interface GreetingGeneratorSubInterface extends GreetingGeneratorInterface {
116+
}
117+
118+
public interface AnotherInterface {
119+
}
120+
121+
public interface AnotherSubInterface extends AnotherInterface {
122+
}
123+
124+
@Unremovable
125+
@ApplicationScoped
126+
public static class GermanGreetingGenerator implements GreetingGeneratorSubInterface, AnotherSubInterface {
127+
128+
@Context
129+
HttpHeaders headers;
130+
}
131+
}

independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,12 @@ public Optional<ResourceClass> createEndpoints(ClassInfo classInfo, boolean cons
297297
}
298298
clazz.setPath(sanitizePath(path));
299299
}
300-
if (factoryCreator != null && !classInfo.isInterface()) {
301-
// Most likely an interface for a sub resource. The ResourceLocatorHandler does not use the factory to create new instances, but uses the result of the sub resource locator method instead
300+
if (factoryCreator != null && !classInfo.isInterface() && !classInfo.isAbstract()) {
301+
// Most likely an interface or an abstract class in the hierarchy of a sub resource.
302+
// The ResourceLocatorHandler does not use the factory to create new instances, but uses the result of the sub resource locator method instead
302303
// Interfaces therefore do not need a factory here
303-
// Otherwise, when having multiple implementations of the interface, an Ambiguous Bean Resolution error occurs, since io.quarkus.arc.runtime.BeanContainerImpl.createFactory is run, even if the factory is never invoked
304+
// Otherwise, when having multiple implementations of the interface or abstract class, an Ambiguous Bean Resolution error occurs,
305+
// since io.quarkus.arc.runtime.BeanContainerImpl.createFactory is run, even if the factory is never invoked
304306
clazz.setFactory(factoryCreator.apply(classInfo.name().toString()));
305307
}
306308
Map<String, String> classLevelExceptionMappers = this.classLevelExceptionMappers.get(classInfo.name());

0 commit comments

Comments
 (0)