Skip to content

Commit

Permalink
[FR] MVC Add method meta data to route fix #2629
Browse files Browse the repository at this point in the history
  • Loading branch information
jknack committed May 21, 2023
1 parent 1271dba commit ed1aca2
Show file tree
Hide file tree
Showing 16 changed files with 351 additions and 23 deletions.
34 changes: 21 additions & 13 deletions jooby/src/main/java/io/jooby/Jooby.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.Spliterator;
Expand Down Expand Up @@ -460,16 +461,7 @@ public Jooby mvc(@NonNull Class router) {
@NonNull @Override
public <T> Jooby mvc(@NonNull Class<T> router, @NonNull Provider<T> provider) {
try {
ServiceLoader<MvcFactory> modules = ServiceLoader.load(MvcFactory.class);
MvcFactory module =
stream(modules.spliterator(), false)
.filter(it -> it.supports(router))
.findFirst()
.orElseGet(
() ->
/** Make happy IDE incremental build: */
mvcReflectionFallback(router, getClassLoader())
.orElseThrow(() -> Usage.mvcRouterNotFound(router)));
MvcFactory module = loadModule(router);
Extension extension = module.create(provider);
extension.install(this);
return this;
Expand All @@ -478,6 +470,22 @@ public <T> Jooby mvc(@NonNull Class<T> router, @NonNull Provider<T> provider) {
}
}

private MvcFactory loadModule(Class router) {
try {
ServiceLoader<MvcFactory> modules = ServiceLoader.load(MvcFactory.class);
return stream(modules.spliterator(), false)
.filter(it -> it.supports(router))
.findFirst()
.orElseGet(
() ->
/** Make happy IDE incremental build: */
mvcReflectionFallback(router, getClassLoader()));
} catch (ServiceConfigurationError notfound) {
/** Make happy IDE incremental build: */
return mvcReflectionFallback(router, getClassLoader());
}
}

@NonNull @Override
public Route ws(@NonNull String pattern, @NonNull WebSocket.Initializer handler) {
return router.ws(pattern, handler);
Expand Down Expand Up @@ -1398,15 +1406,15 @@ private void joobyRunHook(ClassLoader loader, Server server) {
* @param classLoader Class loader.
* @return Mvc factory.
*/
private Optional<MvcFactory> mvcReflectionFallback(Class source, ClassLoader classLoader) {
private MvcFactory mvcReflectionFallback(Class source, ClassLoader classLoader) {
try {
String moduleName = source.getName() + "$Module";
Class<?> moduleType = classLoader.loadClass(moduleName);
Constructor<?> constructor = moduleType.getDeclaredConstructor();
getLog().debug("Loading mvc using reflection: " + source);
return Optional.of((MvcFactory) constructor.newInstance());
return (MvcFactory) constructor.newInstance();
} catch (Exception x) {
return Optional.empty();
throw Usage.mvcRouterNotFound(source);
}
}

Expand Down
33 changes: 33 additions & 0 deletions jooby/src/main/java/io/jooby/Route.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package io.jooby;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -411,6 +412,8 @@ public interface Handler extends Serializable, Aware {

private Boolean nonBlocking;

private Method mvcMethod;

/**
* Creates a new route.
*
Expand Down Expand Up @@ -1023,6 +1026,36 @@ public boolean isTransactional(boolean defaultValue) {
"Invalid value for route attribute " + Transactional.ATTRIBUTE + ": " + attribute);
}

/**
* Method for MVC/Controller. Not available for lambda routes.
*
* @return Method for MVC/Controller. Not available for lambda routes.
*/
public @Nullable Method getMvcMethod() {
return mvcMethod;
}

/**
* Set mvc/controller method.
*
* @param mvcMethod Mvc/controller method.
* @return This route
*/
public @NonNull Route setMvcMethod(@Nullable Method mvcMethod) {
this.mvcMethod = mvcMethod;
return this;
}

/**
* Set mvc/controller method.
*
* @param mvcMethod Mvc/controller method.
* @return This route
*/
public @NonNull Route mvcMethod(@Nullable Method mvcMethod) {
return setMvcMethod(mvcMethod);
}

@Override
public String toString() {
return method + " " + pattern;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public Set<String> getSupportedOptions() {
@Override
public Set<String> getSupportedAnnotationTypes() {
return Stream.concat(Annotations.PATH.stream(), Annotations.HTTP_METHODS.stream())
.collect(Collectors.toCollection(LinkedHashSet::new));
.collect(Collectors.toSet());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ public static MethodDescriptor setReturnType() {
"io.jooby.Route");
}

public static MethodDescriptor setMvcMethod() {
return new MethodDescriptor(
JoobyTypes.Route,
"setMvcMethod",
java.lang.reflect.Method.class.getName(),
"io.jooby.Route");
}

public static MethodDescriptor setExecutorKey() {
return new MethodDescriptor(
JoobyTypes.Route, "setExecutorKey", String.class.getName(), "io.jooby.Route");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
package io.jooby.internal.apt;

import static io.jooby.apt.JoobyProcessor.propagate;
import static io.jooby.internal.apt.JoobyTypes.MvcFactory;
import static io.jooby.internal.apt.JoobyTypes.StatusCode;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
Expand All @@ -31,6 +32,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
Expand Down Expand Up @@ -225,6 +227,13 @@ private void install(ClassWriter writer, List<HandlerCompiler> handlers) throws
*/
setReturnType(visitor, handler);

/**
* ******************************************************************************************
* Mvc Method:
* ******************************************************************************************
*/
setMvcMethod(visitor, handler);

/**
* ******************************************************************************************
* Consumes and Produces
Expand Down Expand Up @@ -255,6 +264,53 @@ private void install(ClassWriter writer, List<HandlerCompiler> handlers) throws
visitor.visitEnd();
}

private void setMvcMethod(MethodVisitor visitor, HandlerCompiler handler) {
visitor.visitVarInsn(ALOAD, 2);
visitor.visitLdcInsn(handler.getController().toJvmType());
ExecutableElement executable = handler.getExecutable();
visitor.visitLdcInsn(executable.getSimpleName().toString());
var args =
executable.getParameters().stream()
.map(it -> ParamDefinition.create(processingEnv, it))
.map(ParamDefinition::getType)
.collect(Collectors.toUnmodifiableList());

ArrayWriter.write(
visitor,
java.lang.Class.class.getName(),
args,
type -> {
if (type.isPrimitive()) {
try {
Method wrapper = Primitives.wrapper(type);
visitor.visitFieldInsn(
GETSTATIC,
Type.getInternalName(wrapper.getDeclaringClass()),
"TYPE",
"Ljava/lang/Class;");
} catch (NoSuchMethodException x) {
propagate(x);
}
} else {
visitor.visitLdcInsn(type.toJvmType());
}
});

visitor.visitMethodInsn(
INVOKEVIRTUAL,
"java/lang/Class",
"getMethod",
"(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
false);
visitor.visitMethodInsn(
INVOKEVIRTUAL,
"io/jooby/Route",
MethodDescriptor.Route.setMvcMethod().getName(),
MethodDescriptor.Route.setMvcMethod().getDescriptor(),
false);
visitor.visitInsn(POP);
}

private void setDispatch(MethodVisitor visitor, ExecutableElement executable)
throws NoSuchMethodException {
String executorKey =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,22 @@
import org.objectweb.asm.Opcodes;

public class ArrayWriter {

public static <T> void write(
MethodVisitor visitor, String componentType, List<T> items, Consumer<T> foreach) {
visitor.visitInsn(Opcodes.ICONST_0 + items.size());
if (items.size() > Opcodes.ICONST_5 - Opcodes.ICONST_0) {
visitor.visitIntInsn(Opcodes.BIPUSH, items.size());
} else {
visitor.visitInsn(Opcodes.ICONST_0 + items.size());
}
visitor.visitTypeInsn(Opcodes.ANEWARRAY, componentType.replace(".", "/"));
for (int i = 0; i < items.size(); i++) {
visitor.visitInsn(Opcodes.DUP);
visitor.visitInsn(Opcodes.ICONST_0 + i);
if (i > Opcodes.ICONST_5 - Opcodes.ICONST_0) {
visitor.visitIntInsn(Opcodes.BIPUSH, i);
} else {
visitor.visitInsn(Opcodes.ICONST_0 + i);
}
foreach.accept(items.get(i));
visitor.visitInsn(Opcodes.AASTORE);
}
Expand Down
4 changes: 2 additions & 2 deletions modules/jooby-apt/src/test/java/output/ASMPrinter.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class ASMPrinter {

@Test
public void mvcExtension() throws Exception {
// ASMifier.main(new String[]{MvcExtension.class.getName()});
// ASMifier.main(new String[]{MvcExtension.class.getName()});
}

@Test
Expand All @@ -21,7 +21,7 @@ public void mvcDispatch() throws Exception {

@Test
public void myController() throws Exception {
// ASMifier.main(new String[]{MyControllerHandler.class.getName()});
// ASMifier.main(new String[]{MyControllerHandler.class.getName()});
}

@Test
Expand Down
6 changes: 3 additions & 3 deletions modules/jooby-apt/src/test/java/output/MvcExtension.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import io.jooby.Jooby;
import io.jooby.MvcFactory;
import jakarta.inject.Provider;
import source.Controller1527;

public class MvcExtension implements MvcFactory {

Expand All @@ -20,10 +19,11 @@ private static void install(Jooby application, Provider<MyController> provider)
"/mypath",
ctx -> {
MyController myController = provider.get();
myController.controllerMethod();
myController.controllerMethod("xx", 1);
return ctx;
})
.attribute("RequireRole", Controller1527.Role.USER);
.mvcMethod(
MyController.class.getDeclaredMethod("controllerMethod", String.class, int.class));
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion modules/jooby-apt/src/test/java/output/MyController.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
public class MyController {

@GET("/default")
public StatusCode controllerMethod() {
public StatusCode controllerMethod(String p, int c) {
return StatusCode.CREATED;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public MyControllerHandler(Provider<MyController> provider) {

@NonNull @Override
public Object apply(@NonNull Context ctx) throws Exception {
StatusCode statusCode = provider.get().controllerMethod();
StatusCode statusCode = provider.get().controllerMethod("ss", 1);
ctx.setResponseCode(statusCode);
return statusCode;
}
Expand Down
19 changes: 19 additions & 0 deletions modules/jooby-apt/src/test/java/tests/i2629/C2629.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Jooby https://jooby.io
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
* Copyright 2014 Edgar Espina
*/
package tests.i2629;

import java.util.List;

import io.jooby.annotation.GET;
import io.jooby.annotation.QueryParam;

public class C2629 {
@GET("/2629")
public String queryUsers(
@QueryParam String type, @QueryParam List<Integer> number, @QueryParam boolean bool) {
return type + ":" + number + ":" + bool;
}
}
31 changes: 31 additions & 0 deletions modules/jooby-apt/src/test/java/tests/i2629/C2629b.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Jooby https://jooby.io
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
* Copyright 2014 Edgar Espina
*/
package tests.i2629;

import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import io.jooby.Context;
import io.jooby.annotation.GET;
import io.jooby.annotation.QueryParam;

public class C2629b {

@GET("/2629")
public String mix(
@QueryParam String s,
@QueryParam Integer i,
@QueryParam double d,
Context ctx,
@QueryParam long j,
@QueryParam Float f,
@QueryParam boolean b) {
return Stream.of(s, i, d, ctx.getMethod(), j, f, b)
.map(Objects::toString)
.collect(Collectors.joining("/"));
}
}
Loading

0 comments on commit ed1aca2

Please sign in to comment.