Skip to content

Commit ea5b018

Browse files
committed
Thrown an error when a private method has MVC annotations fix #372
1 parent 8d8077b commit ea5b018

2 files changed

Lines changed: 94 additions & 15 deletions

File tree

jooby/src/main/java/org/jooby/internal/mvc/MvcRoutes.java

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.lang.annotation.Annotation;
2222
import java.lang.reflect.AnnotatedElement;
2323
import java.lang.reflect.Method;
24+
import java.lang.reflect.Modifier;
2425
import java.util.ArrayList;
2526
import java.util.Arrays;
2627
import java.util.HashMap;
@@ -29,6 +30,8 @@
2930
import java.util.Map;
3031
import java.util.Optional;
3132
import java.util.Set;
33+
import java.util.function.BiConsumer;
34+
import java.util.function.Consumer;
3235
import java.util.function.Function;
3336

3437
import org.jooby.Env;
@@ -71,31 +74,27 @@ public class MvcRoutes {
7174
.add(Consumes.class)
7275
.build();
7376

74-
@SuppressWarnings({"unchecked", "rawtypes" })
7577
public static List<Route.Definition> routes(final Env env, final RouteMetadata classInfo,
7678
final String rpath, final Class<?> routeClass) {
7779

80+
// check and fail fast
81+
methods(routeClass, methods -> {
82+
routes(methods, (m, a) -> {
83+
if (!Modifier.isPublic(m.getModifiers())) {
84+
throw new IllegalArgumentException("Not a public method: " + m);
85+
}
86+
});
87+
});
88+
7889
RequestParamProvider provider = new RequestParamProviderImpl(
7990
new RequestParamNameProviderImpl(classInfo));
8091

8192
String[] rootPaths = path(routeClass);
8293
String[] rootExcludes = excludes(routeClass, EMPTY);
8394

95+
// we are good, now collect them
8496
Map<Method, List<Class<?>>> methods = new HashMap<>();
85-
for (Method method : routeClass.getMethods()) {
86-
List<Class<?>> annotations = new ArrayList<>();
87-
for (Class annotationType : VERBS) {
88-
Annotation annotation = method.getAnnotation(annotationType);
89-
if (annotation != null) {
90-
annotations.add(annotationType);
91-
}
92-
}
93-
if (annotations.size() > 0) {
94-
methods.put(method, annotations);
95-
} else if (method.isAnnotationPresent(Path.class)) {
96-
methods.put(method, Arrays.asList(GET.class));
97-
}
98-
}
97+
routes(routeClass.getMethods(), methods::put);
9998

10099
List<Definition> definitions = new ArrayList<>();
101100
Map<String, Object> attrs = attrs(routeClass.getAnnotations());
@@ -145,6 +144,32 @@ public static List<Route.Definition> routes(final Env env, final RouteMetadata c
145144
return definitions;
146145
}
147146

147+
private static void methods(final Class<?> clazz, final Consumer<Method[]> callback) {
148+
if (clazz != Object.class) {
149+
callback.accept(clazz.getDeclaredMethods());
150+
methods(clazz.getSuperclass(), callback);
151+
}
152+
}
153+
154+
@SuppressWarnings({"rawtypes", "unchecked" })
155+
private static void routes(final Method[] methods,
156+
final BiConsumer<Method, List<Class<?>>> consumer) {
157+
for (Method method : methods) {
158+
List<Class<?>> annotations = new ArrayList<>();
159+
for (Class annotationType : VERBS) {
160+
Annotation annotation = method.getAnnotation(annotationType);
161+
if (annotation != null) {
162+
annotations.add(annotationType);
163+
}
164+
}
165+
if (annotations.size() > 0) {
166+
consumer.accept(method, annotations);
167+
} else if (method.isAnnotationPresent(Path.class)) {
168+
consumer.accept(method, Arrays.asList(GET.class));
169+
}
170+
}
171+
}
172+
148173
private static Map<String, Object> attrs(final Annotation[] annotations) {
149174
Map<String, Object> result = new LinkedHashMap<>();
150175
for (Annotation annotation : annotations) {
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package org.jooby.issues;
2+
3+
import static org.easymock.EasyMock.expect;
4+
5+
import org.jooby.Env;
6+
import org.jooby.Result;
7+
import org.jooby.Results;
8+
import org.jooby.internal.RouteMetadata;
9+
import org.jooby.internal.mvc.MvcRoutes;
10+
import org.jooby.mvc.GET;
11+
import org.jooby.mvc.Path;
12+
import org.jooby.test.MockUnit;
13+
import org.junit.Test;
14+
15+
public class Issue372 {
16+
17+
public static class PingRoute {
18+
@Path("/ping")
19+
@GET
20+
private Result ping() {
21+
return Results.ok();
22+
}
23+
}
24+
25+
public static class Ext extends PingRoute {
26+
}
27+
28+
@Test(expected = IllegalArgumentException.class)
29+
public void shouldFailFastOnPrivateMvcRoutes() throws Exception {
30+
new MockUnit(Env.class)
31+
.expect(unit -> {
32+
Env env = unit.get(Env.class);
33+
expect(env.name()).andReturn("dev").times(2);
34+
})
35+
.run(unit -> {
36+
Env env = unit.get(Env.class);
37+
MvcRoutes.routes(env, new RouteMetadata(env), "", PingRoute.class);
38+
});
39+
}
40+
41+
@Test(expected = IllegalArgumentException.class)
42+
public void shouldFailFastOnPrivateMvcRoutesExt() throws Exception {
43+
new MockUnit(Env.class)
44+
.expect(unit -> {
45+
Env env = unit.get(Env.class);
46+
expect(env.name()).andReturn("dev").times(2);
47+
})
48+
.run(unit -> {
49+
Env env = unit.get(Env.class);
50+
MvcRoutes.routes(env, new RouteMetadata(env), "", Ext.class);
51+
});
52+
}
53+
54+
}

0 commit comments

Comments
 (0)