Skip to content

Commit a2af6e8

Browse files
committed
Added executor registry + MVC Dispatch annotation
1 parent 26c2c00 commit a2af6e8

16 files changed

Lines changed: 400 additions & 20 deletions

File tree

TODO

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
* tests and coverage
22

3-
* get executor module from 1.x into 2.x core
4-
* dispatch on mvc route via @Dispatch, allow to dispatch annotation to use an executor by name
53
* flash scope
64
* make reset headers on error configurable
75

docs/asciidoc/execution-model.adoc

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class App extends Jooby {
3131
}
3232
3333
public static void main(String[] args) {
34-
runApp(EVENT_LOOP, args, App::new);
34+
runApp(args, EVENT_LOOP, App::new);
3535
}
3636
}
3737
----
@@ -43,7 +43,7 @@ import io.jooby.ExecutionMode.EVENT_LOOP
4343
import io.jooby.Jooby.runApp
4444
4545
fun main(args: Array<String>) {
46-
runApp(EVENT_LOOP, args) {
46+
runApp(args, EVENT_LOOP) {
4747
get("/") { ctx -> "I'm non-blocking!" }
4848
}
4949
}
@@ -83,7 +83,7 @@ public class App extends Jooby {
8383
}
8484
8585
public static void main(String[] args) {
86-
runApp(EVENT_LOOP, args, App::new);
86+
runApp(args, EVENT_LOOP, App::new);
8787
}
8888
}
8989
----
@@ -95,7 +95,7 @@ import io.jooby.ExecutionMode.EVENT_LOOP
9595
import io.jooby.Jooby.runApp
9696
9797
fun main(args: Array<String>) {
98-
runApp(EVENT_LOOP, args) {
98+
runApp(args, EVENT_LOOP) {
9999
100100
get("/") { ctx ->
101101
"I'm non-blocking!"
@@ -145,7 +145,7 @@ public class App extends Jooby {
145145
}
146146
147147
public static void main(String[] args) {
148-
runApp(EVENT_LOOP, args, App:new);
148+
runApp(args, EVENT_LOOP, App:new);
149149
}
150150
}
151151
----
@@ -157,7 +157,7 @@ import io.jooby.ExecutionMode.EVENT_LOOP
157157
import io.jooby.Jooby.runApp
158158
159159
fun main(args: Array<String>) {
160-
runApp(EVENT_LOOP, args) {
160+
runApp(args, EVENT_LOOP) {
161161
162162
// Application level executor
163163
worker(Executors.newCachedThreadPool())
@@ -198,7 +198,7 @@ public class App extends Jooby {
198198
}
199199
200200
public static void main(String[] args) {
201-
runApp(WORKER, args, App::new);
201+
runApp(args, WORKER, App::new);
202202
}
203203
}
204204
----
@@ -210,7 +210,7 @@ import io.jooby.ExecutionMode.WORKER
210210
import io.jooby.Jooby.runApp
211211
212212
fun main(args: Array<String>) {
213-
runApp(WORKER, args) {
213+
runApp(args, WORKER) {
214214
215215
get("/") { ctx ->
216216
/** Safe to block! */
@@ -242,7 +242,7 @@ public class App extends Jooby {
242242
}
243243
244244
public static void main(String[] args) {
245-
runApp(WORKER, args, App::new);
245+
runApp(args, WORKER, App::new);
246246
}
247247
}
248248
----
@@ -254,7 +254,7 @@ import io.jooby.ExecutionMode.WORKER
254254
import io.jooby.Jooby.runApp
255255
256256
fun main(args: Array<String>) {
257-
runApp(WORKER, args) {
257+
runApp(args, WORKER) {
258258
259259
worker(Executors.newCachedThreadPool())
260260

docs/asciidoc/mvc-api.adoc

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,175 @@ The javadoc:Jooby[mvc, javax.inject.Provider] does the same job, might or might
401401
instantiation to a dependency injection framework but most important let you control lifecycle of
402402
MVC routes (Singleton vs Non-Singleton routes).
403403

404+
=== Execution model
405+
406+
The MVC routes follows the execution model described in <<Execution Model>>. To run application
407+
logic in the javadoc:ExecutionMode[EVENT_LOOP]:
408+
409+
.EventLoop MVC route
410+
[source, java, role = "primary"]
411+
----
412+
413+
public class App extends Jooby {
414+
{
415+
mvc(new MyController());
416+
}
417+
418+
public static void main(String[] args) {
419+
runApp(args, EVENT_LOOP, App::new); <1>
420+
}
421+
}
422+
----
423+
424+
.Kotlin
425+
[source, kotlin, role = "secondary"]
426+
----
427+
import io.jooby.*
428+
429+
fun main(args: Array<String>) {
430+
runApp(args, EVENT_LOOP) { <1>
431+
mvc(MyController())
432+
}
433+
}
434+
----
435+
436+
<1> Start the application in the EVENT_LOOP execution mode
437+
438+
Similarly, if you need to run all mvc routes in the javadoc:ExecutionMode[WORKER] execution mode:
439+
440+
.Worker mode MVC route
441+
[source, java, role = "primary"]
442+
----
443+
444+
public class App extends Jooby {
445+
{
446+
dispatch(() -> {
447+
mvc(new MyBlockingController()); <1>
448+
});
449+
}
450+
451+
public static void main(String[] args) {
452+
runApp(args, EVENT_LOOP, App::new);
453+
}
454+
}
455+
----
456+
457+
.Kotlin
458+
[source, kotlin, role = "secondary"]
459+
----
460+
import io.jooby.*
461+
462+
fun main(args: Array<String>) {
463+
runApp(args, EVENT_LOOP) {
464+
dispatch {
465+
mvc(MyBlockingController()) <1>
466+
}
467+
}
468+
}
469+
----
470+
471+
<1> Wrap the controller using the dispatch operator
472+
473+
One drawback with this approach is that the entire controller is now going to be executed in the worker or custom executor.
474+
For more fine grain control use the javadoc:annotations.Dispatch[] annotation:
475+
476+
.Dispatch annotation
477+
[source, java, role = "primary"]
478+
----
479+
480+
public class MyController {
481+
@GET("/nonblocking")
482+
public String nonblocking() { <1>
483+
return "I'm nonblocking";
484+
}
485+
486+
@GET("/blocking")
487+
@Dispatch
488+
public String blocking() { <2>
489+
return "I'm blocking";
490+
}
491+
}
492+
----
493+
494+
.Kotlin
495+
[source, kotlin, role = "secondary"]
496+
----
497+
import io.jooby.annotations.*
498+
499+
class MyController {
500+
501+
@GET("/nonblocking")
502+
fun nonblocking() : String { <1>
503+
return "I'm nonblocking";
504+
}
505+
506+
@GET("/blocking")
507+
@Dispatch
508+
fun blocking() : String { <2>
509+
return "I'm blocking";
510+
}
511+
}
512+
----
513+
514+
<1> MVC route run in EVENT_LOOP mode. Blocking is NOT allowed it.
515+
<2> MVC route run in WORKER mode. Blocking is allowed it.
516+
517+
The javadoc:annotations.Dispatch[] annotation supports custom executor using an executor name.
518+
519+
.Dispatch to custom executor
520+
[source, java, role = "primary"]
521+
----
522+
523+
public class MyController {
524+
@GET("/blocking")
525+
@Dispatch("single") <1>
526+
public String blocking() {
527+
return "I'm blocking";
528+
}
529+
}
530+
----
531+
532+
.Kotlin
533+
[source, kotlin, role = "secondary"]
534+
----
535+
import io.jooby.annotations.*
536+
537+
class MyController {
538+
539+
@GET("/blocking")
540+
@Dispatch("single") <1>
541+
fun blocking() : String {
542+
return "I'm blocking";
543+
}
544+
}
545+
----
546+
547+
<1> Dispatch to an executor named it `single`
548+
549+
Executor must be registered using via services or executor utility method:
550+
551+
.Custom executor registration
552+
[source, java, role = "primary"]
553+
----
554+
{
555+
executor("single", Executors.newSingleThreadExecutor());
556+
557+
mvc(new MyController());
558+
}
559+
----
560+
561+
.Kotlin
562+
[source, kotlin, role = "secondary"]
563+
----
564+
{
565+
executor("single", Executors.newSingleThreadExecutor())
566+
567+
mvc(MyController())
568+
}
569+
----
570+
571+
The executor must be registered before the MVC route/controller.
572+
404573
=== Suspend functions
405574

406575
For Kotlin users MVC routes are allowed to use suspend functions

examples/src/main/java/examples/DispatchApp.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ public class DispatchApp extends Jooby {
3030
dispatch(() -> {
3131
get("/worker", ctx -> ctx.query("n").intValue(2));
3232
});
33+
34+
executor("single", Executors.newSingleThreadExecutor());
35+
dispatch(() -> {
36+
get("/worker", ctx -> ctx.query("n").intValue(2));
37+
});
3338
}
3439

3540
public static void main(String[] args) {

examples/src/main/java/examples/MvcApp.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,18 @@
88
import io.jooby.ExecutionMode;
99
import io.jooby.Jooby;
1010

11+
import java.util.concurrent.ExecutorService;
12+
import java.util.concurrent.Executors;
13+
1114
public class MvcApp extends Jooby {
1215

1316
{
17+
ExecutorService single = Executors.newSingleThreadExecutor();
18+
executor("single", single);
19+
20+
ExecutorService cached = Executors.newCachedThreadPool();
21+
executor("cached", cached);
22+
1423
mvc(new PlainText());
1524
}
1625

examples/src/main/java/examples/PlainText.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,27 @@
55
*/
66
package examples;
77

8+
import io.jooby.annotations.Dispatch;
89
import io.jooby.annotations.GET;
9-
import io.jooby.annotations.Path;
1010
import io.jooby.annotations.PathParam;
1111

1212
import java.util.Optional;
1313

14+
@Dispatch
1415
public class PlainText {
15-
@GET
16-
@Path("/plaintext")
16+
@GET("/plaintext")
1717
public String plainText(@PathParam Optional<String> message) {
1818
return message.orElse("Hello, World!");
1919
}
20+
21+
@GET("/single")
22+
@Dispatch("single")
23+
public String single() {
24+
return Thread.currentThread().getName();
25+
}
26+
27+
@GET("/")
28+
public String loop() {
29+
return Thread.currentThread().getName();
30+
}
2031
}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import java.util.LinkedList;
2525
import java.util.List;
2626
import java.util.Map;
27-
import java.util.Optional;
2827
import java.util.Properties;
2928
import java.util.ServiceLoader;
3029
import java.util.Spliterator;
@@ -498,6 +497,14 @@ public Jooby errorCode(@Nonnull Class<? extends Throwable> type,
498497
return this;
499498
}
500499

500+
@Nonnull @Override public Jooby executor(@Nonnull String name, @Nonnull Executor executor) {
501+
if (executor instanceof ExecutorService) {
502+
onStop(((ExecutorService) executor)::shutdown);
503+
}
504+
router.executor(name, executor);
505+
return this;
506+
}
507+
501508
/**
502509
* Start application, find a web server, deploy application, start router, extension modules,
503510
* etc..

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,12 @@ default Router error(@Nonnull Predicate<StatusCode> predicate,
634634
*/
635635
@Nonnull Router setSessionOptions(@Nonnull SessionOptions options);
636636

637+
default @Nonnull Executor executor(@Nonnull String name) {
638+
return require(Executor.class, name);
639+
}
640+
641+
@Nonnull Router executor(@Nonnull String name, @Nonnull Executor executor);
642+
637643
/**
638644
* Normalize a path by removing consecutives <code>/</code>(slashes), make it lower case and
639645
* removing trailing slash.

0 commit comments

Comments
 (0)