Skip to content

Commit c68de34

Browse files
add type verification for flash/session/cookie/header mvc params
1 parent 8e3555b commit c68de34

File tree

11 files changed

+208
-49
lines changed

11 files changed

+208
-49
lines changed

modules/jooby-apt/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@
5858
<scope>test</scope>
5959
</dependency>
6060

61+
<dependency>
62+
<groupId>org.junit.jupiter</groupId>
63+
<artifactId>junit-jupiter-params</artifactId>
64+
<scope>test</scope>
65+
</dependency>
66+
6167
<dependency>
6268
<groupId>org.jacoco</groupId>
6369
<artifactId>org.jacoco.agent</artifactId>

modules/jooby-apt/src/main/java/io/jooby/internal/apt/MvcParameter.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,11 @@ yield hasAnnotation(NULLABLE)
100100
yield ParameterGenerator.BodyParam.toSourceCode(
101101
kt, route, null, type, parameterName, isNullable(kt));
102102
} else {
103-
yield strategy
104-
.get()
105-
.getKey()
106-
.toSourceCode(
107-
kt, route, strategy.get().getValue(), type, parameterName, isNullable(kt));
103+
var paramGenerator = strategy.get().getKey();
104+
paramGenerator.verifyType(parameterType, parameterName, route);
105+
106+
yield paramGenerator.toSourceCode(
107+
kt, route, strategy.get().getValue(), type, parameterName, isNullable(kt));
108108
}
109109
}
110110
};

modules/jooby-apt/src/main/java/io/jooby/internal/apt/ParameterGenerator.java

Lines changed: 24 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,9 @@
66
package io.jooby.internal.apt;
77

88
import static io.jooby.internal.apt.AnnotationSupport.findAnnotationValue;
9+
import static io.jooby.internal.apt.Types.BUILT_IN;
910
import static java.util.stream.Collectors.joining;
1011

11-
import java.net.URI;
12-
import java.net.URL;
13-
import java.time.Duration;
14-
import java.time.Period;
15-
import java.time.ZoneId;
1612
import java.util.*;
1713
import java.util.function.Predicate;
1814
import java.util.stream.Stream;
@@ -53,10 +49,10 @@ public String toSourceCode(
5349
}
5450
}
5551
},
56-
CookieParam("cookie", "io.jooby.annotation.CookieParam", "jakarta.ws.rs.CookieParam"),
57-
FlashParam("flash", "io.jooby.annotation.FlashParam"),
52+
CookieParam("cookie", BUILT_IN, "io.jooby.annotation.CookieParam", "jakarta.ws.rs.CookieParam"),
53+
FlashParam("flash", BUILT_IN, "io.jooby.annotation.FlashParam"),
5854
FormParam("form", "io.jooby.annotation.FormParam", "jakarta.ws.rs.FormParam"),
59-
HeaderParam("header", "io.jooby.annotation.HeaderParam", "jakarta.ws.rs.HeaderParam"),
55+
HeaderParam("header", BUILT_IN, "io.jooby.annotation.HeaderParam", "jakarta.ws.rs.HeaderParam"),
6056
Lookup("lookup", "io.jooby.annotation.Param") {
6157
@Override
6258
protected Predicate<String> namePredicate() {
@@ -65,7 +61,7 @@ protected Predicate<String> namePredicate() {
6561
},
6662
PathParam("path", "io.jooby.annotation.PathParam", "jakarta.ws.rs.PathParam"),
6763
QueryParam("query", "io.jooby.annotation.QueryParam", "jakarta.ws.rs.QueryParam"),
68-
SessionParam("session", "io.jooby.annotation.SessionParam"),
64+
SessionParam("session", BUILT_IN, "io.jooby.annotation.SessionParam"),
6965
BodyParam("body") {
7066
@Override
7167
public String parameterName(AnnotationMirror annotation, String defaultParameterName) {
@@ -368,6 +364,24 @@ public static ParameterGenerator findByAnnotation(String annotation) {
368364
this.annotations = Set.of(annotations);
369365
}
370366

367+
ParameterGenerator(String method, Set<String> typeRestrictions, String... annotations) {
368+
this(method, annotations);
369+
this.typeRestrictions = typeRestrictions;
370+
}
371+
372+
public void verifyType(String type, String parameterName, MvcRoute route) {
373+
if (!typeRestrictions.isEmpty()) {
374+
if (typeRestrictions.stream().noneMatch(type::equals)) {
375+
throw new IllegalArgumentException("""
376+
Illegal argument type at '%s.%s()'.\s
377+
Parameter '%s' annotated as %s cannot be of type '%s'.\s
378+
Supported types are: %s
379+
""".formatted(route.getRouter().getTargetType().toString(),
380+
route.getMethodName(), parameterName, annotations, type, Types.BUILT_IN));
381+
}
382+
}
383+
}
384+
371385
protected String source(AnnotationMirror annotation) {
372386
if (ParameterGenerator.Lookup.annotations.contains(annotation.getAnnotationType().toString())) {
373387
var sources = findAnnotationValue(annotation, AnnotationSupport.VALUE);
@@ -382,40 +396,6 @@ protected String source(AnnotationMirror annotation) {
382396

383397
protected final String method;
384398
private final Set<String> annotations;
399+
private Set<String> typeRestrictions = Set.of(); // empty set means no restrictions by default
385400
private static final Set<Class> CONTAINER = Set.of(List.class, Set.class, Optional.class);
386-
private static final Set<String> BUILT_IN =
387-
Set.of(
388-
String.class.getName(),
389-
Boolean.class.getName(),
390-
Boolean.TYPE.getName(),
391-
Byte.class.getName(),
392-
Byte.TYPE.getName(),
393-
Character.class.getName(),
394-
Character.TYPE.getName(),
395-
Short.class.getName(),
396-
Short.TYPE.getName(),
397-
Integer.class.getName(),
398-
Integer.TYPE.getName(),
399-
Long.class.getName(),
400-
Long.TYPE.getName(),
401-
Float.class.getName(),
402-
Float.TYPE.getName(),
403-
Double.class.getName(),
404-
Double.TYPE.getName(),
405-
Enum.class.getName(),
406-
java.util.UUID.class.getName(),
407-
java.time.Instant.class.getName(),
408-
java.util.Date.class.getName(),
409-
java.time.LocalDate.class.getName(),
410-
java.time.LocalDateTime.class.getName(),
411-
java.math.BigDecimal.class.getName(),
412-
java.math.BigInteger.class.getName(),
413-
Duration.class.getName(),
414-
Period.class.getName(),
415-
java.nio.charset.Charset.class.getName(),
416-
"io.jooby.StatusCode",
417-
TimeZone.class.getName(),
418-
ZoneId.class.getName(),
419-
URI.class.getName(),
420-
URL.class.getName());
421401
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal.apt;
7+
8+
import java.net.URI;
9+
import java.net.URL;
10+
import java.time.Duration;
11+
import java.time.Period;
12+
import java.time.ZoneId;
13+
import java.util.Set;
14+
import java.util.TimeZone;
15+
16+
class Types {
17+
static final Set<String> BUILT_IN =
18+
Set.of(
19+
String.class.getName(),
20+
Boolean.class.getName(),
21+
Boolean.TYPE.getName(),
22+
Byte.class.getName(),
23+
Byte.TYPE.getName(),
24+
Character.class.getName(),
25+
Character.TYPE.getName(),
26+
Short.class.getName(),
27+
Short.TYPE.getName(),
28+
Integer.class.getName(),
29+
Integer.TYPE.getName(),
30+
Long.class.getName(),
31+
Long.TYPE.getName(),
32+
Float.class.getName(),
33+
Float.TYPE.getName(),
34+
Double.class.getName(),
35+
Double.TYPE.getName(),
36+
Enum.class.getName(),
37+
java.util.UUID.class.getName(),
38+
java.time.Instant.class.getName(),
39+
java.util.Date.class.getName(),
40+
java.time.LocalDate.class.getName(),
41+
java.time.LocalDateTime.class.getName(),
42+
java.math.BigDecimal.class.getName(),
43+
java.math.BigInteger.class.getName(),
44+
Duration.class.getName(),
45+
Period.class.getName(),
46+
java.nio.charset.Charset.class.getName(),
47+
"io.jooby.StatusCode",
48+
TimeZone.class.getName(),
49+
ZoneId.class.getName(),
50+
URI.class.getName(),
51+
URL.class.getName());
52+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package tests.verifyarg;
7+
8+
import io.jooby.annotation.CookieParam;
9+
import io.jooby.annotation.GET;
10+
11+
class ControllerCookie {
12+
@GET
13+
public void param(@CookieParam Object param) {
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package tests.verifyarg;
7+
8+
import io.jooby.annotation.FlashParam;
9+
import io.jooby.annotation.GET;
10+
11+
class ControllerFlash {
12+
@GET
13+
public void param(@FlashParam Object param) {
14+
}
15+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package tests.verifyarg;
7+
8+
import io.jooby.annotation.FlashParam;
9+
import io.jooby.annotation.GET;
10+
11+
import java.util.Optional;
12+
13+
class ControllerFlashOpt {
14+
@GET
15+
public void param(@FlashParam Optional<Object> param) {
16+
}
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package tests.verifyarg;
7+
8+
import io.jooby.annotation.GET;
9+
import jakarta.ws.rs.HeaderParam;
10+
11+
class ControllerHeader {
12+
@GET
13+
public void param(@HeaderParam("test") Object param) {
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package tests.verifyarg;
7+
8+
import io.jooby.annotation.GET;
9+
import io.jooby.annotation.SessionParam;
10+
11+
class ControllerSession {
12+
@GET
13+
public void param(@SessionParam Object param) {
14+
}
15+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package tests.verifyarg;
7+
8+
import io.jooby.apt.ProcessorRunner;
9+
import org.junit.jupiter.params.ParameterizedTest;
10+
import org.junit.jupiter.params.provider.Arguments;
11+
import org.junit.jupiter.params.provider.MethodSource;
12+
13+
import java.util.List;
14+
15+
import static org.junit.jupiter.api.Assertions.assertThrows;
16+
import static org.junit.jupiter.api.Assertions.assertTrue;
17+
18+
class VerifyArgTypeTest {
19+
20+
@ParameterizedTest
21+
@MethodSource("provideControllers")
22+
public void compileController_illegalArgumentType_shouldThrowException(Object controller) {
23+
RuntimeException ex = assertThrows(RuntimeException.class,
24+
() -> new ProcessorRunner(controller));
25+
26+
assertTrue(ex.getMessage().contains("Illegal argument type at"));
27+
}
28+
29+
private static List<Arguments> provideControllers() {
30+
return List.of(
31+
Arguments.of(new ControllerFlash()),
32+
Arguments.of(new ControllerFlashOpt()),
33+
Arguments.of(new ControllerCookie()),
34+
Arguments.of(new ControllerSession()),
35+
Arguments.of(new ControllerHeader())
36+
);
37+
}
38+
}

0 commit comments

Comments
 (0)