Skip to content

Commit 6c169af

Browse files
authored
Merge pull request #34237 from Sgitario/33865
Fail when using collections in Resteasy Reactive JAXB
2 parents 8e3bb8b + 20d286d commit 6c169af

File tree

5 files changed

+201
-12
lines changed

5 files changed

+201
-12
lines changed

extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/main/java/io/quarkus/resteasy/reactive/jaxb/deployment/ResteasyReactiveJaxbProcessor.java

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.quarkus.resteasy.reactive.jaxb.deployment;
22

3+
import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.REST_RESPONSE;
4+
35
import java.util.ArrayList;
46
import java.util.Collection;
57
import java.util.Collections;
@@ -8,6 +10,7 @@
810
import java.util.Locale;
911
import java.util.Set;
1012

13+
import jakarta.enterprise.inject.spi.DeploymentException;
1114
import jakarta.ws.rs.Priorities;
1215
import jakarta.ws.rs.RuntimeType;
1316
import jakarta.ws.rs.core.MediaType;
@@ -19,7 +22,10 @@
1922
import org.jboss.jandex.FieldInfo;
2023
import org.jboss.jandex.IndexView;
2124
import org.jboss.jandex.MethodInfo;
25+
import org.jboss.jandex.MethodParameterInfo;
2226
import org.jboss.jandex.Type;
27+
import org.jboss.resteasy.reactive.common.model.MethodParameter;
28+
import org.jboss.resteasy.reactive.common.model.ParameterType;
2329
import org.jboss.resteasy.reactive.common.model.ResourceMethod;
2430
import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames;
2531

@@ -87,6 +93,13 @@ void registerClassesToBeBound(ResteasyReactiveResourceMethodEntriesBuildItem res
8793
if (effectiveReturnType != null) {
8894
// When using "application/xml", the return type needs to be registered
8995
if (producesXml(resourceInfo)) {
96+
if (!isTypeCompatibleWithJaxb(methodInfo.returnType())) {
97+
throw new DeploymentException(
98+
"Cannot directly return collections or arrays using JAXB. You need to wrap it "
99+
+ "into a root element class. Problematic method is '"
100+
+ entry.getActualClassInfo().name() + "." + methodInfo.name() + "'");
101+
}
102+
90103
classesInfo.add(effectiveReturnType);
91104
}
92105

@@ -100,8 +113,16 @@ void registerClassesToBeBound(ResteasyReactiveResourceMethodEntriesBuildItem res
100113
boolean consumesXml = consumesXml(resourceInfo);
101114
boolean consumesMultipart = consumesMultipart(resourceInfo);
102115
if (consumesXml || consumesMultipart) {
103-
for (Type parameter : methodInfo.parameterTypes()) {
104-
ClassInfo effectiveParameter = getEffectiveClassInfo(parameter, indexView);
116+
for (MethodParameterInfo parameter : methodInfo.parameters()) {
117+
if (isParameterBody(parameter, resourceInfo) && !isTypeCompatibleWithJaxb(parameter.type())) {
118+
throw new DeploymentException(
119+
"Cannot handle collections or arrays as parameters using JAXB. You need to wrap it "
120+
+ "into a root element class. Problematic parameter is '" + parameter.name()
121+
+ "' in the method '" + entry.getActualClassInfo().name() + "." + methodInfo.name()
122+
+ "'");
123+
}
124+
125+
ClassInfo effectiveParameter = getEffectiveClassInfo(parameter.type(), indexView);
105126
if (effectiveParameter != null) {
106127
if (consumesXml) {
107128
classesInfo.add(effectiveParameter);
@@ -154,23 +175,16 @@ private ClassInfo getEffectiveClassInfo(Type type, IndexView indexView) {
154175
}
155176

156177
Type effectiveType = type;
157-
if (effectiveType.name().equals(ResteasyReactiveDotNames.REST_RESPONSE) ||
158-
effectiveType.name().equals(ResteasyReactiveDotNames.UNI) ||
159-
effectiveType.name().equals(ResteasyReactiveDotNames.COMPLETABLE_FUTURE) ||
160-
effectiveType.name().equals(ResteasyReactiveDotNames.COMPLETION_STAGE) ||
161-
effectiveType.name().equals(ResteasyReactiveDotNames.REST_MULTI) ||
162-
effectiveType.name().equals(ResteasyReactiveDotNames.MULTI)) {
178+
if (isContainerType(effectiveType)) {
163179
if (effectiveType.kind() != Type.Kind.PARAMETERIZED_TYPE) {
164180
return null;
165181
}
166182

167183
effectiveType = type.asParameterizedType().arguments().get(0);
168184
}
169-
if (effectiveType.name().equals(ResteasyReactiveDotNames.SET) ||
170-
effectiveType.name().equals(ResteasyReactiveDotNames.COLLECTION) ||
171-
effectiveType.name().equals(ResteasyReactiveDotNames.LIST)) {
185+
if (isCollectionType(effectiveType)) {
172186
effectiveType = effectiveType.asParameterizedType().arguments().get(0);
173-
} else if (effectiveType.name().equals(ResteasyReactiveDotNames.MAP)) {
187+
} else if (isMapType(effectiveType)) {
174188
effectiveType = effectiveType.asParameterizedType().arguments().get(1);
175189
}
176190

@@ -220,4 +234,53 @@ private List<String> toClasses(Collection<ClassInfo> classesInfo) {
220234

221235
return classes;
222236
}
237+
238+
private boolean isParameterBody(MethodParameterInfo parameter, ResourceMethod resourceInfo) {
239+
for (MethodParameter parameterInfo : resourceInfo.getParameters()) {
240+
if (parameterInfo.name != null && parameterInfo.name.equals(parameter.name())) {
241+
return parameterInfo.parameterType == ParameterType.BODY;
242+
}
243+
}
244+
245+
return false;
246+
}
247+
248+
private boolean isCollectionType(Type type) {
249+
return type.name().equals(ResteasyReactiveDotNames.SET) ||
250+
type.name().equals(ResteasyReactiveDotNames.COLLECTION) ||
251+
type.name().equals(ResteasyReactiveDotNames.LIST);
252+
}
253+
254+
private boolean isMapType(Type type) {
255+
return type.name().equals(ResteasyReactiveDotNames.MAP);
256+
}
257+
258+
private boolean isContainerType(Type type) {
259+
return type.name().equals(REST_RESPONSE) ||
260+
type.name().equals(ResteasyReactiveDotNames.UNI) ||
261+
type.name().equals(ResteasyReactiveDotNames.COMPLETABLE_FUTURE) ||
262+
type.name().equals(ResteasyReactiveDotNames.COMPLETION_STAGE) ||
263+
type.name().equals(ResteasyReactiveDotNames.REST_MULTI) ||
264+
type.name().equals(ResteasyReactiveDotNames.MULTI);
265+
}
266+
267+
private boolean isTypeCompatibleWithJaxb(Type type) {
268+
if (type.kind() == Type.Kind.PRIMITIVE) {
269+
return true;
270+
}
271+
272+
if (type.kind() == Type.Kind.ARRAY || isCollectionType(type) || isMapType(type)) {
273+
return false;
274+
}
275+
276+
if (isContainerType(type)) {
277+
if (type.kind() != Type.Kind.PARAMETERIZED_TYPE) {
278+
return true;
279+
}
280+
281+
return isTypeCompatibleWithJaxb(type.asParameterizedType().arguments().get(0));
282+
}
283+
284+
return true;
285+
}
223286
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package io.quarkus.resteasy.reactive.jaxb.deployment.test;
2+
3+
import java.util.List;
4+
5+
import jakarta.enterprise.inject.spi.DeploymentException;
6+
import jakarta.ws.rs.GET;
7+
import jakarta.ws.rs.Path;
8+
import jakarta.ws.rs.Produces;
9+
import jakarta.ws.rs.core.MediaType;
10+
11+
import org.jboss.shrinkwrap.api.ShrinkWrap;
12+
import org.jboss.shrinkwrap.api.spec.JavaArchive;
13+
import org.junit.jupiter.api.Assertions;
14+
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.api.extension.RegisterExtension;
16+
17+
import io.quarkus.test.QuarkusUnitTest;
18+
19+
public class FailWhenReturnListTest {
20+
21+
@RegisterExtension
22+
static QuarkusUnitTest test = new QuarkusUnitTest()
23+
.setExpectedException(DeploymentException.class)
24+
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
25+
.addClasses(GreetingResource.class));
26+
27+
@Test
28+
void shouldFailWithDeploymentException() {
29+
Assertions.fail("The test case should not be invoked as it should fail with a deployment exception.");
30+
}
31+
32+
@Path("/greeting")
33+
public static class GreetingResource {
34+
35+
@GET
36+
@Produces(MediaType.APPLICATION_XML)
37+
public List<String> hello() {
38+
return List.of("1", "2", "3");
39+
}
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package io.quarkus.resteasy.reactive.jaxb.deployment.test;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
import jakarta.enterprise.inject.spi.DeploymentException;
7+
import jakarta.ws.rs.GET;
8+
import jakarta.ws.rs.Path;
9+
import jakarta.ws.rs.Produces;
10+
import jakarta.ws.rs.core.MediaType;
11+
12+
import org.jboss.resteasy.reactive.RestResponse;
13+
import org.jboss.shrinkwrap.api.ShrinkWrap;
14+
import org.jboss.shrinkwrap.api.spec.JavaArchive;
15+
import org.junit.jupiter.api.Assertions;
16+
import org.junit.jupiter.api.Test;
17+
import org.junit.jupiter.api.extension.RegisterExtension;
18+
19+
import io.quarkus.test.QuarkusUnitTest;
20+
21+
public class FailWhenReturnRestResponseTest {
22+
23+
@RegisterExtension
24+
static QuarkusUnitTest test = new QuarkusUnitTest()
25+
.setExpectedException(DeploymentException.class)
26+
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
27+
.addClasses(GreetingResource.class));
28+
29+
@Test
30+
void shouldFailWithDeploymentException() {
31+
Assertions.fail("The test case should not be invoked as it should fail with a deployment exception.");
32+
}
33+
34+
@Path("/greeting")
35+
public static class GreetingResource {
36+
37+
@GET
38+
@Produces(MediaType.APPLICATION_XML)
39+
public RestResponse<Map<String, String>> hello() {
40+
return RestResponse.ok(new HashMap<>());
41+
}
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package io.quarkus.resteasy.reactive.jaxb.deployment.test;
2+
3+
import java.util.List;
4+
5+
import jakarta.enterprise.inject.spi.DeploymentException;
6+
import jakarta.ws.rs.Consumes;
7+
import jakarta.ws.rs.POST;
8+
import jakarta.ws.rs.Path;
9+
import jakarta.ws.rs.core.MediaType;
10+
11+
import org.jboss.shrinkwrap.api.ShrinkWrap;
12+
import org.jboss.shrinkwrap.api.spec.JavaArchive;
13+
import org.junit.jupiter.api.Assertions;
14+
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.api.extension.RegisterExtension;
16+
17+
import io.quarkus.test.QuarkusUnitTest;
18+
19+
public class FailWhenUseListParamTest {
20+
21+
@RegisterExtension
22+
static QuarkusUnitTest test = new QuarkusUnitTest()
23+
.setExpectedException(DeploymentException.class)
24+
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
25+
.addClasses(GreetingResource.class));
26+
27+
@Test
28+
void shouldFailWithDeploymentException() {
29+
Assertions.fail("The test case should not be invoked as it should fail with a deployment exception.");
30+
}
31+
32+
@Path("/greeting")
33+
public static class GreetingResource {
34+
35+
@POST
36+
@Consumes(MediaType.APPLICATION_XML)
37+
public String hello(List<String> items) {
38+
return "ok";
39+
}
40+
}
41+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,7 @@ && isParameterContainerType(paramType.asClassType())) {
13341334
if (field) {
13351335
return builder;
13361336
}
1337+
builder.setName(sourceName);
13371338
builder.setType(ParameterType.BODY);
13381339
}
13391340
}

0 commit comments

Comments
 (0)