From 51d5937b07a744e28e69e6f4d969df513f3787ed Mon Sep 17 00:00:00 2001 From: Edgar Espina Date: Thu, 19 Sep 2024 10:32:11 -0300 Subject: [PATCH] Add MvcMethod object for setMvcMethod and getMvcMethod. - fix #3532 - jooby-apt option will be off by default in next major release --- jooby/src/main/java/io/jooby/Route.java | 60 +++++++++++++++++-- .../io/jooby/internal/apt/MvcContext.java | 2 +- .../java/io/jooby/internal/apt/MvcRoute.java | 9 ++- .../src/test/java/tests/i2629/Issue2629.java | 16 +++-- .../src/test/java/tests/i2629/Issue2629b.java | 16 +++-- 5 files changed, 85 insertions(+), 18 deletions(-) diff --git a/jooby/src/main/java/io/jooby/Route.java b/jooby/src/main/java/io/jooby/Route.java index 6d320ae57b..351aa142b5 100644 --- a/jooby/src/main/java/io/jooby/Route.java +++ b/jooby/src/main/java/io/jooby/Route.java @@ -6,6 +6,9 @@ package io.jooby; import java.io.Serializable; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; @@ -370,12 +373,55 @@ public interface Handler extends Serializable, Aware { if (contentType == null) { throw new UnsupportedMediaType(null); } - if (!ctx.getRoute().getConsumes().stream().anyMatch(contentType::matches)) { + if (ctx.getRoute().getConsumes().stream().noneMatch(contentType::matches)) { throw new UnsupportedMediaType(contentType.getValue()); } } }; + /** + * Carry metadata for mvc/controller method. + * + * @param declaringClass Controller class. + * @param name Method name. + * @param returnType Method return type. + * @param parameterTypes Method argument types. + */ + public record MvcMethod( + @NonNull Class declaringClass, + @NonNull String name, + @NonNull Class returnType, + Class... parameterTypes) { + + /** + * Convert to {@link java.lang.reflect.Method}. + * + * @return A {@link java.lang.reflect.Method}. + */ + public Method toMethod() { + try { + return declaringClass.getDeclaredMethod(name, parameterTypes); + } catch (NoSuchMethodException e) { + throw SneakyThrows.propagate(e); + } + } + + /** + * Convert to {@link MethodHandle}. + * + * @return A {@link MethodHandle}. + */ + public MethodHandle toMethodHandle() { + var lookup = MethodHandles.publicLookup(); + var methodType = MethodType.methodType(returnType, parameterTypes); + try { + return lookup.findVirtual(declaringClass, name, methodType); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw SneakyThrows.propagate(e); + } + } + } + /** Favicon handler as a silent 404 error. */ public static final Handler FAVICON = ctx -> ctx.send(StatusCode.NOT_FOUND); @@ -423,7 +469,7 @@ public interface Handler extends Serializable, Aware { private Boolean nonBlocking; - private Method mvcMethod; + private MvcMethod mvcMethod; private boolean httpHead; @@ -655,7 +701,9 @@ public boolean isNonBlockingSet() { * Route return type. * * @return Return type. + * @deprecated Marked for removal on 4.0 */ + @Deprecated public @Nullable Type getReturnType() { return returnType; } @@ -665,7 +713,9 @@ public boolean isNonBlockingSet() { * * @param returnType Return type. * @return This route. + * @deprecated Marked for removal on 4.0 */ + @Deprecated public @NonNull Route setReturnType(@Nullable Type returnType) { this.returnType = returnType; return this; @@ -1046,7 +1096,7 @@ public boolean isTransactional(boolean defaultValue) { * * @return Method for MVC/Controller. Not available for lambda routes. */ - public @Nullable Method getMvcMethod() { + public @Nullable MvcMethod getMvcMethod() { return mvcMethod; } @@ -1056,7 +1106,7 @@ public boolean isTransactional(boolean defaultValue) { * @param mvcMethod Mvc/controller method. * @return This route */ - public @NonNull Route setMvcMethod(@Nullable Method mvcMethod) { + public @NonNull Route setMvcMethod(@Nullable MvcMethod mvcMethod) { this.mvcMethod = mvcMethod; return this; } @@ -1067,7 +1117,7 @@ public boolean isTransactional(boolean defaultValue) { * @param mvcMethod Mvc/controller method. * @return This route */ - public @NonNull Route mvcMethod(@Nullable Method mvcMethod) { + public @NonNull Route mvcMethod(@Nullable MvcMethod mvcMethod) { return setMvcMethod(mvcMethod); } diff --git a/modules/jooby-apt/src/main/java/io/jooby/internal/apt/MvcContext.java b/modules/jooby-apt/src/main/java/io/jooby/internal/apt/MvcContext.java index 1ab605191c..0a3e24e953 100644 --- a/modules/jooby-apt/src/main/java/io/jooby/internal/apt/MvcContext.java +++ b/modules/jooby-apt/src/main/java/io/jooby/internal/apt/MvcContext.java @@ -45,7 +45,7 @@ public MvcContext( this.debug = Options.boolOpt(processingEnvironment, Options.DEBUG, false); this.incremental = Options.boolOpt(processingEnvironment, Options.INCREMENTAL, true); this.returnType = Options.boolOpt(processingEnvironment, Options.RETURN_TYPE, false); - this.mvcMethod = Options.boolOpt(processingEnvironment, Options.MVC_METHOD, false); + this.mvcMethod = Options.boolOpt(processingEnvironment, Options.MVC_METHOD, true); this.services = Options.boolOpt(processingEnvironment, Options.SERVICES, true); this.routerPrefix = Options.string(processingEnvironment, Options.ROUTER_PREFIX, ""); this.routerSuffix = Options.string(processingEnvironment, Options.ROUTER_SUFFIX, "_"); diff --git a/modules/jooby-apt/src/main/java/io/jooby/internal/apt/MvcRoute.java b/modules/jooby-apt/src/main/java/io/jooby/internal/apt/MvcRoute.java index a1e2d147bd..8df7d431b8 100644 --- a/modules/jooby-apt/src/main/java/io/jooby/internal/apt/MvcRoute.java +++ b/modules/jooby-apt/src/main/java/io/jooby/internal/apt/MvcRoute.java @@ -156,10 +156,15 @@ public List generateMapping(boolean kt) { CodeBlock.of( indent(2), ".setMvcMethod(", - router.getTargetType().getSimpleName(), + kt ? "" : "new ", + "io.jooby.Route.MvcMethod(", + router.getTargetType().getQualifiedName().toString(), clazz(kt), - ".getMethod(", + ", ", string(getMethodName()), + ", ", + type(kt, returnType.getRawType().toString()), + clazz(kt), paramString.isEmpty() ? "" : ", " + paramString, "))", semicolon(kt), diff --git a/modules/jooby-apt/src/test/java/tests/i2629/Issue2629.java b/modules/jooby-apt/src/test/java/tests/i2629/Issue2629.java index 75e4a8aca6..e73ddaa72e 100644 --- a/modules/jooby-apt/src/test/java/tests/i2629/Issue2629.java +++ b/modules/jooby-apt/src/test/java/tests/i2629/Issue2629.java @@ -5,8 +5,7 @@ */ package tests.i2629; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.*; import java.util.List; import java.util.Map; @@ -30,9 +29,16 @@ public void shouldSetMvcMethod() throws Exception { assertEquals("foo:[12]:true", router.get("/2629", ctx).value().toString()); var route = app.getRoutes().get(0); assertNotNull(route.getMvcMethod()); - assertEquals( - "foo:[14]:false", - route.getMvcMethod().invoke(new C2629(), "foo", List.of(14), false)); + try { + assertEquals( + "foo:[14]:false", + route + .getMvcMethod() + .toMethodHandle() + .invoke(new C2629(), "foo", List.of(14), Boolean.FALSE)); + } catch (Throwable cause) { + fail(cause); + } }); } } diff --git a/modules/jooby-apt/src/test/java/tests/i2629/Issue2629b.java b/modules/jooby-apt/src/test/java/tests/i2629/Issue2629b.java index 8afb134b32..72ebb778dc 100644 --- a/modules/jooby-apt/src/test/java/tests/i2629/Issue2629b.java +++ b/modules/jooby-apt/src/test/java/tests/i2629/Issue2629b.java @@ -5,8 +5,7 @@ */ package tests.i2629; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.*; import java.util.Map; @@ -29,9 +28,16 @@ public void shouldSetMvcMethod() throws Exception { assertEquals("foo/1/2.0/GET/3/4.0/true", router.get("/2629", ctx).value().toString()); var route = app.getRoutes().get(0); assertNotNull(route.getMvcMethod()); - assertEquals( - "bar/5/6.0/GET/7/8.0/false", - route.getMvcMethod().invoke(new C2629b(), "bar", 5, 6d, ctx, 7l, 8f, false)); + try { + assertEquals( + "bar/5/6.0/GET/7/8.0/false", + route + .getMvcMethod() + .toMethodHandle() + .invoke(new C2629b(), "bar", 5, 6d, ctx, 7l, 8f, false)); + } catch (Throwable e) { + fail(e); + } }); } }