Skip to content

Commit 0dfd5fb

Browse files
committed
Fix ambiguous bean resolution for sub resource interfaces with multiple implementations
1 parent 30f2f55 commit 0dfd5fb

File tree

2 files changed

+132
-1
lines changed
  • extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic
  • independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor

2 files changed

+132
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package io.quarkus.resteasy.reactive.server.test.resource.basic;
2+
3+
import static io.restassured.RestAssured.given;
4+
import static org.hamcrest.CoreMatchers.is;
5+
6+
import java.util.Map;
7+
import java.util.function.Supplier;
8+
9+
import jakarta.enterprise.context.RequestScoped;
10+
import jakarta.inject.Inject;
11+
import jakarta.ws.rs.GET;
12+
import jakarta.ws.rs.Path;
13+
import jakarta.ws.rs.PathParam;
14+
import jakarta.ws.rs.Produces;
15+
import jakarta.ws.rs.core.MediaType;
16+
17+
import org.jboss.shrinkwrap.api.ShrinkWrap;
18+
import org.jboss.shrinkwrap.api.spec.JavaArchive;
19+
import org.junit.jupiter.api.Test;
20+
import org.junit.jupiter.api.extension.RegisterExtension;
21+
22+
import io.quarkus.resteasy.reactive.server.test.simple.PortProviderUtil;
23+
import io.quarkus.test.QuarkusUnitTest;
24+
25+
class SubResourceAmbiguousInjectTest {
26+
@RegisterExtension
27+
static QuarkusUnitTest testExtension = new QuarkusUnitTest()
28+
.setArchiveProducer(new Supplier<>() {
29+
@Override
30+
public JavaArchive get() {
31+
JavaArchive war = ShrinkWrap.create(JavaArchive.class);
32+
war.addClass(PortProviderUtil.class);
33+
war.addClass(EnglishGreeterResource.class);
34+
war.addClass(SpanishGreeterResource.class);
35+
war.addClass(GreeterResource.class);
36+
war.addClass(LanguageResource.class);
37+
war.addClass(LanguageResourceV2.class);
38+
war.addClass(GreeterResourceV2.class);
39+
war.addClass(EnglishGreeterResource.class);
40+
war.addClass(SpanishGreeterResource.class);
41+
return war;
42+
}
43+
});
44+
45+
@Test
46+
void basicTest() {
47+
given().when().get("/languages/en").then().statusCode(200).body(is("hello"));
48+
given().when().get("/languages/v2/es").then().statusCode(200).body(is("hola"));
49+
}
50+
51+
@RequestScoped
52+
public static class EnglishGreeterResource implements GreeterResource {
53+
@Override
54+
public String greeting() {
55+
return "hello";
56+
}
57+
}
58+
59+
@RequestScoped
60+
public static class SpanishGreeterResource implements GreeterResource {
61+
@Override
62+
public String greeting() {
63+
return "hola";
64+
}
65+
}
66+
67+
public interface GreeterResource {
68+
@GET
69+
@Produces(MediaType.TEXT_PLAIN)
70+
String greeting();
71+
}
72+
73+
@Path("languages")
74+
public static class LanguageResource {
75+
76+
private final Map<String, GreeterResource> languages;
77+
78+
@Inject
79+
public LanguageResource(
80+
final EnglishGreeterResource english, final SpanishGreeterResource spanish) {
81+
languages = Map.of("en", english, "es", spanish);
82+
}
83+
84+
@Path("{language}")
85+
public GreeterResource locateGreeter(@PathParam("language") final String language) {
86+
return languages.get(language);
87+
}
88+
}
89+
90+
@Path("languages/v2")
91+
public static class LanguageResourceV2 {
92+
93+
private final Map<String, GreeterResourceV2> languages;
94+
95+
@Inject
96+
public LanguageResourceV2(
97+
final EnglishGreeterResourceV2 english, final SpanishGreeterResourceV2 spanish) {
98+
languages = Map.of("en", english, "es", spanish);
99+
}
100+
101+
@Path("{language}")
102+
public GreeterResourceV2 locateGreeter(@PathParam("language") final String language) {
103+
return languages.get(language);
104+
}
105+
}
106+
107+
public abstract static class GreeterResourceV2 {
108+
@GET
109+
@Produces(MediaType.TEXT_PLAIN)
110+
public abstract String greeting();
111+
}
112+
113+
@RequestScoped
114+
public static class EnglishGreeterResourceV2 extends GreeterResourceV2 {
115+
@Override
116+
public String greeting() {
117+
return "hello";
118+
}
119+
}
120+
121+
@RequestScoped
122+
public static class SpanishGreeterResourceV2 extends GreeterResourceV2 {
123+
@Override
124+
public String greeting() {
125+
return "hola";
126+
}
127+
}
128+
}

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,10 @@ public Optional<ResourceClass> createEndpoints(ClassInfo classInfo, boolean cons
297297
}
298298
clazz.setPath(sanitizePath(path));
299299
}
300-
if (factoryCreator != null) {
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
302+
// 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
301304
clazz.setFactory(factoryCreator.apply(classInfo.name().toString()));
302305
}
303306
Map<String, String> classLevelExceptionMappers = this.classLevelExceptionMappers.get(classInfo.name());

0 commit comments

Comments
 (0)