Skip to content

Commit 19fa17c

Browse files
committed
APT: fix random bug on JDK 11 related to annotation and defalts value
1 parent 1716ba2 commit 19fa17c

File tree

4 files changed

+51
-23
lines changed

4 files changed

+51
-23
lines changed

modules/jooby-apt/src/main/java/io/jooby/apt/JoobyProcessor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
9696
.forEach(e -> routeMap.computeIfAbsent(e, k -> new LinkedHashMap<>()));
9797

9898
if (Annotations.HTTP_METHODS.contains(annotation.asType().toString())) {
99-
List<ExecutableElement> methods = elements.stream()
99+
Set<ExecutableElement> methods = elements.stream()
100100
.filter(ExecutableElement.class::isInstance)
101101
.map(ExecutableElement.class::cast)
102-
.collect(Collectors.toList());
102+
.collect(Collectors.toCollection(LinkedHashSet::new));
103103
for (ExecutableElement method : methods) {
104104
Map<TypeElement, List<ExecutableElement>> mapping = routeMap
105105
.computeIfAbsent(method.getEnclosingElement(), k -> new LinkedHashMap<>());

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,18 @@ public class ModuleCompiler {
5959
private final String moduleClass;
6060
private final String moduleInternalName;
6161
private final String moduleJava;
62-
private final ProcessingEnvironment env;
62+
private final ProcessingEnvironment processingEnv;
6363
private final String moduleDescriptorName;
64+
private final boolean debug;
6465

65-
public ModuleCompiler(ProcessingEnvironment env, String controllerClass) {
66+
public ModuleCompiler(ProcessingEnvironment processingEnv, String controllerClass) {
6667
this.controllerClass = controllerClass;
6768
this.moduleClass = this.controllerClass + "$Module";
6869
this.moduleJava = this.moduleClass + ".java";
6970
this.moduleInternalName = moduleClass.replace(".", "/");
7071
this.moduleDescriptorName = "L" + moduleInternalName + ";";
71-
this.env = env;
72+
this.processingEnv = processingEnv;
73+
this.debug = Boolean.parseBoolean(processingEnv.getOptions().getOrDefault("debug", "false"));
7274
}
7375

7476
public String getModuleClass() {
@@ -156,8 +158,8 @@ private void install(ClassWriter writer, List<HandlerCompiler> handlers) throws
156158
visitor.visitParameter("provider", 0);
157159
visitor.visitCode();
158160

159-
RouteAttributesWriter routeAttributes = new RouteAttributesWriter(env.getElementUtils(),
160-
env.getTypeUtils(), writer, moduleInternalName, visitor);
161+
RouteAttributesWriter routeAttributes = new RouteAttributesWriter(processingEnv.getElementUtils(),
162+
processingEnv.getTypeUtils(), writer, moduleInternalName, visitor);
161163

162164
Map<String, Integer> nameRegistry = new HashMap<>();
163165
for (HandlerCompiler handler : handlers) {
@@ -200,7 +202,9 @@ private void install(ClassWriter writer, List<HandlerCompiler> handlers) throws
200202
* Annotations as route attributes
201203
* ******************************************************************************************
202204
*/
203-
routeAttributes.process(handler.getExecutable());
205+
debug("route attributes %s.%s", handler.getExecutable().getEnclosingElement(),
206+
handler.getExecutable());
207+
routeAttributes.process(handler.getExecutable(), this::debug);
204208

205209
/**
206210
* ******************************************************************************************
@@ -237,7 +241,7 @@ private void setDispatch(MethodVisitor visitor, ExecutableElement executable)
237241
}
238242

239243
private Object annotationAttribute(AnnotationMirror annotationMirror, String method) {
240-
Map<? extends ExecutableElement, ? extends AnnotationValue> map = env
244+
Map<? extends ExecutableElement, ? extends AnnotationValue> map = processingEnv
241245
.getElementUtils().getElementValuesWithDefaults(annotationMirror);
242246
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : map.entrySet()) {
243247
if (entry.getKey().getSimpleName().toString().equals(method)) {
@@ -329,4 +333,10 @@ private void supports(ClassWriter writer) {
329333
visitor.visitMaxs(0, 0);
330334
visitor.visitEnd();
331335
}
336+
337+
private void debug(String format, Object... args) {
338+
if (debug) {
339+
System.out.printf(format + "\n", args);
340+
}
341+
}
332342
}

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

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,12 @@
2929
import java.util.Arrays;
3030
import java.util.Collections;
3131
import java.util.HashMap;
32+
import java.util.LinkedHashMap;
3233
import java.util.List;
3334
import java.util.Map;
3435
import java.util.UUID;
36+
import java.util.function.BiConsumer;
37+
import java.util.function.Consumer;
3538
import java.util.function.Predicate;
3639

3740
import static io.jooby.SneakyThrows.throwingConsumer;
@@ -107,12 +110,14 @@ public RouteAttributesWriter(Elements elements, Types types, ClassWriter writer,
107110
this.visitor = visitor;
108111
}
109112

110-
public void process(ExecutableElement method) throws NoSuchMethodException {
113+
public void process(ExecutableElement method, BiConsumer<String, Object[]> log)
114+
throws NoSuchMethodException {
111115
Method target = Route.class.getDeclaredMethod("attribute", String.class, Object.class);
112116
Map<String, Object> attributes = annotationMap(method);
113117
for (Map.Entry<String, Object> attribute : attributes.entrySet()) {
114118
String name = attribute.getKey();
115119
Object value = attribute.getValue();
120+
log.accept(" %s: %s", new Object[]{name, value});
116121

117122
visitor.visitVarInsn(ALOAD, 2);
118123
visitor.visitLdcInsn(name);
@@ -145,16 +150,26 @@ private Map<String, Object> annotationMap(List<? extends AnnotationMirror> annot
145150
String prefix = root == null
146151
? annotation.getAnnotationType().asElement().getSimpleName().toString()
147152
: root;
148-
Map<? extends ExecutableElement, ? extends AnnotationValue> values = elements
149-
.getElementValuesWithDefaults(annotation);
150-
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> attribute : values
151-
.entrySet()) {
152-
Object value = annotationValue(attribute.getValue());
153-
if (value != null) {
154-
String method = attribute.getKey().getSimpleName().toString();
155-
String name = method.equals("value") ? prefix : prefix + "." + method;
156-
result.put(name, value);
157-
}
153+
// Set all values and then override with present values (fix for JDK 11+)
154+
result.putAll(toMap(elements
155+
.getElementValuesWithDefaults(annotation), prefix));
156+
result.putAll(toMap(annotation.getElementValues(), prefix));
157+
}
158+
return result;
159+
}
160+
161+
private Map<String, Object> toMap(
162+
Map<? extends ExecutableElement, ? extends AnnotationValue> values,
163+
String prefix) {
164+
Map<String, Object> result = new LinkedHashMap<>();
165+
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> attribute : values
166+
.entrySet()) {
167+
Object value = annotationValue(attribute.getValue());
168+
if (value != null) {
169+
String method = attribute.getKey().getSimpleName().toString();
170+
String name = method.equals("value") ? prefix : prefix + "." + method;
171+
// Found value is override on JDK 11 with default annotation value, we trust that spe
172+
result.putIfAbsent(name, value);
158173
}
159174
}
160175
return result;
@@ -194,7 +209,9 @@ private void annotationValue(ClassWriter writer, MethodVisitor visitor, Object v
194209
.visitMethodInsn(INVOKESTATIC, moduleInternalName, newMap, "()Ljava/util/Map;", false);
195210
} else if (value instanceof List) {
196211
List values = (List) value;
197-
String componentType = values.get(0) instanceof EnumValue? ((EnumValue) values.get(0)).type : values.get(0).getClass().getName();
212+
String componentType = values.get(0) instanceof EnumValue ?
213+
((EnumValue) values.get(0)).type :
214+
values.get(0).getClass().getName();
198215
if (values.size() > 0) {
199216
ArrayWriter.write(visitor, componentType, values, throwingConsumer(v ->
200217
annotationValue(writer, visitor, v)
@@ -274,7 +291,8 @@ private void annotationSingleValue(MethodVisitor visitor, Object value)
274291
} else if (value instanceof EnumValue) {
275292
EnumValue enumValue = (EnumValue) value;
276293
Type type = Type.getObjectType(enumValue.type.replace(".", "/"));
277-
visitor.visitFieldInsn(GETSTATIC, type.getInternalName(), enumValue.value, type.getDescriptor());
294+
visitor
295+
.visitFieldInsn(GETSTATIC, type.getInternalName(), enumValue.value, type.getDescriptor());
278296
}
279297

280298
Method wrapper = Primitives.wrapper(value.getClass());

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public void voidRoutes() throws Exception {
8686

8787
@Test
8888
public void getPostRoutes() throws Exception {
89-
new MvcModuleCompilerRunner(new GetPostRoute(), true)
89+
new MvcModuleCompilerRunner(new GetPostRoute())
9090
.module(app -> {
9191
MockRouter router = new MockRouter(app);
9292
router.get("/", rsp -> {

0 commit comments

Comments
 (0)