Skip to content

Commit 5e5d78a

Browse files
committed
@javax.annotation.Nonnull with MVC and @QueryParam
fix jooby-project#1786
1 parent 789a768 commit 5e5d78a

File tree

8 files changed

+125
-0
lines changed

8 files changed

+125
-0
lines changed

jooby/src/main/java/io/jooby/exception/MissingValueException.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,11 @@ public MissingValueException(@Nonnull String name) {
3434
public String getName() {
3535
return name;
3636
}
37+
38+
public static <T> T requireNonNull(String name, T value) {
39+
if (value == null) {
40+
throw new MissingValueException(name);
41+
}
42+
return value;
43+
}
3744
}

modules/jooby-apt/src/main/java/io/jooby/internal/apt/asm/ValueWriter.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
import io.jooby.internal.apt.ParamDefinition;
1010
import org.objectweb.asm.ClassWriter;
1111
import org.objectweb.asm.MethodVisitor;
12+
import org.objectweb.asm.Opcodes;
1213

1314
import java.lang.reflect.Method;
1415
import java.lang.reflect.Type;
1516
import java.util.Map;
1617

18+
import static org.objectweb.asm.Opcodes.ALOAD;
19+
import static org.objectweb.asm.Opcodes.ASTORE;
1720
import static org.objectweb.asm.Opcodes.CHECKCAST;
1821
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
1922
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
@@ -62,6 +65,14 @@ public void accept(ClassWriter writer, String handlerInternalName, MethodVisitor
6265

6366
if (toClass || toReified) {
6467
visitor.visitTypeInsn(CHECKCAST, parameter.getType().toJvmType().getInternalName());
68+
if (!parameter.isNullable()) {
69+
visitor.visitVarInsn(ASTORE, 3);
70+
visitor.visitVarInsn(ALOAD, 2);
71+
visitor.visitLdcInsn(parameter.getHttpName());
72+
visitor.visitVarInsn(ALOAD, 3);
73+
visitor.visitMethodInsn(INVOKESTATIC, "io/jooby/exception/MissingValueException", "requireNonNull", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;", false);
74+
visitor.visitTypeInsn(CHECKCAST, parameter.getType().toJvmType().getInternalName());
75+
}
6576
}
6677
}
6778

modules/jooby-apt/src/test/java/io/jooby/apt/MvcModuleCompilerRunner.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
import io.jooby.MvcFactory;
99
import io.jooby.SneakyThrows;
1010
import io.jooby.internal.converter.ReflectiveBeanConverter;
11+
import org.objectweb.asm.util.ASMifier;
1112

1213
import javax.inject.Provider;
1314
import javax.tools.JavaFileObject;
15+
import java.io.IOException;
1416
import java.lang.reflect.Constructor;
1517
import java.net.MalformedURLException;
1618
import java.nio.file.Files;
@@ -24,6 +26,7 @@
2426
public class MvcModuleCompilerRunner {
2527
private final TestMvcProcessor processor;
2628
private final Object instance;
29+
private final List<String> examples = new ArrayList<>();
2730

2831
public MvcModuleCompilerRunner(Object instance) throws Exception {
2932
this(instance, false);
@@ -55,9 +58,19 @@ public MvcModuleCompilerRunner module(SneakyThrows.Consumer<Jooby> consumer) thr
5558

5659
public MvcModuleCompilerRunner debugModule(SneakyThrows.Consumer<Jooby> consumer)
5760
throws Exception {
61+
for (String example : examples) {
62+
printExample(example);
63+
}
5864
return module(true, consumer);
5965
}
6066

67+
private void printExample(String example) throws IOException {
68+
System.out.println("*************************************************************************");
69+
System.out.println("******************************** Example ********************************");
70+
ASMifier.main(new String[]{example});
71+
System.out.println("*************************************************************************");
72+
}
73+
6174
private MvcModuleCompilerRunner module(boolean debug, SneakyThrows.Consumer<Jooby> consumer)
6275
throws Exception {
6376
Class clazz = instance.getClass();
@@ -101,5 +114,10 @@ private static Path basedir() {
101114
}
102115
return path.resolve("modules").resolve("jooby-apt");
103116
}
117+
118+
public MvcModuleCompilerRunner example(Class example) {
119+
examples.add(example.getName());
120+
return this;
121+
}
104122
}
105123

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package source;
2+
3+
import io.jooby.annotations.GET;
4+
import io.jooby.annotations.QueryParam;
5+
6+
import javax.annotation.Nonnull;
7+
import java.util.UUID;
8+
9+
public class Controller1786b {
10+
11+
@GET("/required-param")
12+
public UUID requiredParam(@QueryParam @Nonnull UUID value) {
13+
return value;
14+
}
15+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package tests;
2+
3+
import io.jooby.Context;
4+
import io.jooby.Extension;
5+
import io.jooby.MvcFactory;
6+
import io.jooby.exception.MissingValueException;
7+
import source.Controller1786b;
8+
9+
import javax.annotation.Nonnull;
10+
import javax.inject.Provider;
11+
import java.util.UUID;
12+
13+
public class Expected1786b implements MvcFactory {
14+
15+
@Override public boolean supports(@Nonnull Class type) {
16+
return false;
17+
}
18+
19+
@Nonnull @Override public Extension create(@Nonnull Provider provider) {
20+
return application -> {
21+
application.get("/required-param", ctx -> {
22+
Controller1786b controller = (Controller1786b) provider.get();
23+
UUID uuid = ctx.query("uuid").to(UUID.class);
24+
return controller.requiredParam(MissingValueException.requireNonNull("uuid", uuid));
25+
});
26+
};
27+
}
28+
29+
}

modules/jooby-apt/src/test/java/tests/Issue1786.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import io.jooby.exception.MissingValueException;
88
import org.junit.jupiter.api.Test;
99
import source.Controller1786;
10+
import source.Controller1786b;
1011

1112
import static org.junit.jupiter.api.Assertions.assertEquals;
1213
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -22,6 +23,16 @@ public void shouldThrowMissingValueExceptionIfRequiredStringParamNotSpecified()
2223
});
2324
}
2425

26+
@Test
27+
public void shouldThrowMissingValueExceptionIfRequiredParamNotSpecified() throws Exception {
28+
new MvcModuleCompilerRunner(new Controller1786b())
29+
.example(Expected1786b.class)
30+
.module(app -> {
31+
MockRouter router = new MockRouter(app);
32+
assertThrows(MissingValueException.class, () -> router.get("/required-param"));
33+
});
34+
}
35+
2536
@Test
2637
public void shouldReturnValueIfRequiredStringParamSpecified() throws Exception {
2738
new MvcModuleCompilerRunner(new Controller1786())
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.jooby.i1786;
2+
3+
import io.jooby.annotations.GET;
4+
import io.jooby.annotations.QueryParam;
5+
6+
import javax.annotation.Nonnull;
7+
import java.util.UUID;
8+
9+
public class Controller1786 {
10+
@GET("/1786/nonnull")
11+
public UUID followNonnullAnnotation(@QueryParam @Nonnull UUID key) {
12+
return key;
13+
}
14+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package io.jooby.i1786;
2+
3+
import io.jooby.StatusCode;
4+
import io.jooby.junit.ServerTest;
5+
import io.jooby.junit.ServerTestRunner;
6+
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
9+
public class Issue1786 {
10+
@ServerTest
11+
public void shouldFollowNonNullAnnotation(ServerTestRunner runner) {
12+
runner.define(app -> {
13+
app.mvc(new Controller1786());
14+
}).ready(client -> {
15+
client.get("/1786/nonnull", rsp -> {
16+
assertEquals(StatusCode.BAD_REQUEST_CODE, rsp.code());
17+
});
18+
});
19+
}
20+
}

0 commit comments

Comments
 (0)