Skip to content

Commit f07d999

Browse files
committed
@ResultType: new annotation for source code generator fix #3447
Hints source code generator (jooby annotation processor) to map/adapt a specific return type to use a custom handler. This annotation if only for source code generator process so only applies for MVC routes. Example: ```java class MyController { @get("/") public MySpecialType hello() {} } } ``` * Write a code generator: ```java @ResultType(types = MySpecialType.class, handler = "customMapping") class MySpecialTypeGenerator { public static Route.Handler customMapping(Route.Handler handler) { return myHandler.then(handler); } } ``` * Let jooby annotation processor to know about your handler by setting the `jooby.handler` annotation processor option: jooby.handler=mypackage.MySpecialTypeGenerator * Generates: ```java app.get("/", customMapping(this::hello)); ``` This new annotation replaces the existing ResultHandler supports which works at runtime, while this one works at compile time. - handler: remove io.jooby.ResultHandler fix #3422
1 parent 1ea138e commit f07d999

File tree

34 files changed

+395
-343
lines changed

34 files changed

+395
-343
lines changed

jooby/src/main/java/io/jooby/Jooby.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -747,12 +747,6 @@ public Logger getLog() {
747747
return LoggerFactory.getLogger(getClass());
748748
}
749749

750-
@NonNull @Override
751-
public Jooby resultHandler(ResultHandler handler) {
752-
router.resultHandler(handler);
753-
return this;
754-
}
755-
756750
@NonNull @Override
757751
public ErrorHandler getErrorHandler() {
758752
return router.getErrorHandler();

jooby/src/main/java/io/jooby/ReactiveSupport.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.concurrent.CompletionStage;
99
import java.util.concurrent.Flow;
1010

11+
import io.jooby.annotation.ResultType;
1112
import io.jooby.internal.handler.ChunkedSubscriber;
1213
import io.jooby.internal.handler.ConcurrentHandler;
1314

@@ -17,6 +18,9 @@
1718
* @author edgar
1819
* @since 3.0.0
1920
*/
21+
@ResultType(
22+
types = {Flow.Publisher.class, CompletionStage.class},
23+
handler = "concurrent")
2024
public class ReactiveSupport {
2125

2226
private static final Route.Filter CONCURRENT = new ConcurrentHandler();
@@ -40,4 +44,8 @@ public static <T> Flow.Subscriber<T> newSubscriber(Context ctx) {
4044
public static Route.Filter concurrent() {
4145
return CONCURRENT;
4246
}
47+
48+
public static Route.Handler concurrent(Route.Handler next) {
49+
return CONCURRENT.then(next);
50+
}
4351
}

jooby/src/main/java/io/jooby/ResultHandler.java

Lines changed: 0 additions & 76 deletions
This file was deleted.

jooby/src/main/java/io/jooby/Route.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,18 @@ public interface Filter extends Aware {
8888
* @return A new handler.
8989
*/
9090
@NonNull default Handler then(@NonNull Handler next) {
91-
return ctx -> apply(next).apply(ctx);
91+
return new Handler() {
92+
@NonNull @Override
93+
public Object apply(@NonNull Context ctx) throws Exception {
94+
return Filter.this.apply(next).apply(ctx);
95+
}
96+
97+
@Override
98+
public void setRoute(Route route) {
99+
Filter.this.setRoute(route);
100+
next.setRoute(route);
101+
}
102+
};
92103
}
93104
}
94105

jooby/src/main/java/io/jooby/Router.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -883,14 +883,6 @@ default Object execute(@NonNull Context context) {
883883
*/
884884
@NonNull Logger getLog();
885885

886-
/**
887-
* Add a response handler factory.
888-
*
889-
* @param factory Response handler factory.
890-
* @return This router.
891-
*/
892-
@NonNull Router resultHandler(@NonNull ResultHandler factory);
893-
894886
/**
895887
* Router options.
896888
*
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.annotation;
7+
8+
import java.lang.annotation.ElementType;
9+
import java.lang.annotation.Retention;
10+
import java.lang.annotation.RetentionPolicy;
11+
import java.lang.annotation.Target;
12+
13+
/**
14+
* Hints source code generator (jooby annotation processor) to map/adapt a specific return type to
15+
* use a custom handler. This annotation if only for source code generator process so only applies
16+
* for MVC routes. Example:
17+
*
18+
* <pre>{@code
19+
* class MyController {
20+
* @GET("/")
21+
* public MySpecialType hello() {}
22+
* }
23+
* }</pre>
24+
*
25+
* Write a code generator:
26+
*
27+
* <pre>{@code
28+
* @ResultType(types = MySpecialType.class, handler = "customMapping")
29+
* class MySpecialTypeGenerator {
30+
*
31+
* public static Route.Handler customMapping(Route.Handler handler) {
32+
* return myHandler.then(handler);
33+
* }
34+
* }
35+
* }</pre>
36+
*
37+
* Let jooby annotation processor to know about your handler by setting the <code>jooby.handler
38+
* </code> annotation processor option: <code>jooby.handler=mypackage.MySpecialTypeGenerator</code>
39+
* Generates:
40+
*
41+
* <pre>{@code
42+
* app.get("/", customMapping(this::hello));
43+
* }</pre>
44+
*
45+
* @author edgar
46+
* @since 3.2.0
47+
*/
48+
@Retention(RetentionPolicy.RUNTIME)
49+
@Target(ElementType.TYPE)
50+
public @interface ResultType {
51+
/**
52+
* Custom type that requires special handling.
53+
*
54+
* @return Types.
55+
*/
56+
Class<?>[] types();
57+
58+
/**
59+
* Mapping function must be: - Single argument function of type {@link io.jooby.Route.Handler}. -
60+
* Returns type {@link io.jooby.Route.Handler}. - Must be static.
61+
*
62+
* @return Name of mapping function.
63+
*/
64+
String handler();
65+
}

jooby/src/main/java/io/jooby/internal/Pipeline.java

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,13 @@
55
*/
66
package io.jooby.internal;
77

8-
import static io.jooby.ReactiveSupport.concurrent;
98
import static io.jooby.internal.handler.DefaultHandler.DEFAULT;
109
import static io.jooby.internal.handler.DetachHandler.DETACH;
11-
import static io.jooby.internal.handler.SendDirect.DIRECT;
1210
import static io.jooby.internal.handler.WorkerHandler.WORKER;
1311

14-
import java.lang.reflect.Type;
15-
import java.util.Set;
16-
import java.util.concurrent.CompletionStage;
1712
import java.util.concurrent.Executor;
18-
import java.util.concurrent.Flow;
1913

20-
import io.jooby.Context;
2114
import io.jooby.ExecutionMode;
22-
import io.jooby.Reified;
23-
import io.jooby.ResultHandler;
2415
import io.jooby.Route;
2516
import io.jooby.Route.Handler;
2617
import io.jooby.internal.handler.DispatchHandler;
@@ -29,53 +20,13 @@
2920
public class Pipeline {
3021

3122
public static Handler build(
32-
Route route,
33-
ExecutionMode mode,
34-
Executor executor,
35-
ContextInitializer initializer,
36-
Set<ResultHandler> responseHandler) {
23+
Route route, ExecutionMode mode, Executor executor, ContextInitializer initializer) {
3724
// Set default wrapper and blocking mode
3825
if (!route.isNonBlockingSet()) {
3926
route.setNonBlocking(isDefaultNonblocking(executor, mode));
4027
}
4128
Route.Filter wrapper = route.isNonBlocking() ? DETACH : DEFAULT;
4229

43-
/** Return type is set by annotation processor, or manually per lambda route: */
44-
Type returnType = route.getReturnType();
45-
if (returnType != null) {
46-
Class<?> type = Reified.rawType(returnType);
47-
/** Context: */
48-
if (Context.class.isAssignableFrom(type)) {
49-
if (route.isNonBlocking()) {
50-
wrapper = DETACH;
51-
} else {
52-
wrapper = DIRECT;
53-
}
54-
} else if (CompletionStage.class.isAssignableFrom(type)
55-
|| Flow.Publisher.class.isAssignableFrom(type)) {
56-
/** Completable future: */
57-
Route.Filter concurrent = concurrent();
58-
// Notify there is a route:
59-
concurrent.setRoute(route);
60-
wrapper = DETACH.then(concurrent);
61-
} else {
62-
/** Custom responses: */
63-
for (ResultHandler factory : responseHandler) {
64-
if (factory.matches(returnType)) {
65-
Route.Filter custom = factory.create();
66-
// Notify there is a route:
67-
custom.setRoute(route);
68-
if (factory.isReactive()) {
69-
// Mark route as reactive
70-
wrapper = DETACH.then(custom);
71-
} else {
72-
wrapper = custom;
73-
}
74-
break;
75-
}
76-
}
77-
}
78-
}
7930
// Non-Blocking? Split pipeline Head+Handler let reactive call After pipeline
8031
Handler pipeline;
8132
if (route.isNonBlocking()) {

jooby/src/main/java/io/jooby/internal/RouterImpl.java

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import java.util.EnumSet;
1818
import java.util.HashMap;
1919
import java.util.LinkedHashMap;
20-
import java.util.LinkedHashSet;
2120
import java.util.LinkedList;
2221
import java.util.List;
2322
import java.util.Locale;
@@ -150,8 +149,6 @@ public Stack executor(Executor executor) {
150149

151150
private Map<String, Object> attributes = new ConcurrentHashMap<>();
152151

153-
private List<ResultHandler> resultHandlers = new ArrayList<>();
154-
155152
private ServiceRegistry services = new ServiceRegistryImpl();
156153

157154
private SessionStore sessionStore = SessionStore.memory();
@@ -638,15 +635,9 @@ private void pureAscii(String pattern, Consumer<String> consumer) {
638635
prependMediaType(route.getConsumes(), route.getFilter(), Route.SUPPORT_MEDIA_TYPE));
639636
route.setFilter(prependMediaType(route.getProduces(), route.getFilter(), Route.ACCEPT));
640637
}
641-
Set<ResultHandler> resultSet = new LinkedHashSet<>();
642-
if (resultHandlers != null) {
643-
resultSet.addAll(resultHandlers);
644-
}
645-
ServiceLoader.load(ResultHandler.class).forEach(resultSet::add);
646638
/** Response handler: */
647639
Route.Handler pipeline =
648-
Pipeline.build(
649-
route, forceMode(route, mode), executor, postDispatchInitializer, resultSet);
640+
Pipeline.build(route, forceMode(route, mode), executor, postDispatchInitializer);
650641
route.setPipeline(pipeline);
651642
/** Final render */
652643
route.setEncoder(encoder);
@@ -780,12 +771,6 @@ public StatusCode errorCode(@NonNull Throwable x) {
780771
return StatusCode.SERVER_ERROR;
781772
}
782773

783-
@NonNull @Override
784-
public Router resultHandler(ResultHandler handler) {
785-
resultHandlers.add(handler);
786-
return this;
787-
}
788-
789774
@NonNull @Override
790775
public ServiceRegistry getServices() {
791776
return services;

jooby/src/main/java/module-info.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
uses io.jooby.MvcFactory;
1616
uses io.jooby.Server;
1717
uses io.jooby.SslProvider;
18-
uses io.jooby.ResultHandler;
1918
uses io.jooby.LoggingService;
2019
uses io.jooby.buffer.DataBufferFactory;
2120

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@
2525
import com.squareup.javapoet.JavaFile;
2626
import io.jooby.internal.apt.*;
2727

28-
@SupportedOptions({DEBUG, INCREMENTAL, SERVICES, SKIP_ATTRIBUTE_ANNOTATIONS})
28+
@SupportedOptions({HANDLER, DEBUG, INCREMENTAL, SERVICES, SKIP_ATTRIBUTE_ANNOTATIONS})
2929
@SupportedSourceVersion(SourceVersion.RELEASE_17)
3030
public class JoobyProcessor extends AbstractProcessor {
3131
public interface Options {
32+
String HANDLER = "jooby.handler";
3233
String DEBUG = "jooby.debug";
3334
String ROUTER_PREFIX = "jooby.routerPrefix";
3435
String ROUTER_SUFFIX = "jooby.routerSuffix";
@@ -283,8 +284,7 @@ public static RuntimeException propagate(final Throwable x) {
283284
throw new NullPointerException("x");
284285
}
285286

286-
sneakyThrow0(x);
287-
return null;
287+
return sneakyThrow0(x);
288288
}
289289

290290
/**
@@ -295,7 +295,7 @@ public static RuntimeException propagate(final Throwable x) {
295295
* @throws E Exception to throw.
296296
*/
297297
@SuppressWarnings("unchecked")
298-
private static <E extends Throwable> void sneakyThrow0(final Throwable x) throws E {
298+
private static <E extends Throwable> E sneakyThrow0(final Throwable x) throws E {
299299
throw (E) x;
300300
}
301301
}

0 commit comments

Comments
 (0)