jooby 2
After a long consideration of existing implementation, ideas described in #926, evaluation of new frameworks (specially from Go but also http4k)... found some time to draw what and how Jooby 2 is going to look and work.
As always, your feedback is welcome.
design
- Single
Context
object replaces Request
and Response
- Route
Handler
is now a function (returns a value)
- Filter is now a function and they are not tied to a
route pattern
. They are group and composed using as functions
- Route
chain
/pipeline
replaced by functional filter
- Run code in the IO Thread
- Router algorithm now matches a single
Handler
. We can't add multiple handlers to same route
- No more greedy Ant path pattern:
**
- There will be a clear difference between
query
, path
and form
parameters. Must get rid of the generic Request.param(String)
method
- Root package will be io.jooby
- Remove DI/Guice from core
contracts
interface Context {
//... merge of Request/Response objects from 1.x
}
interface Handler {
Object apply(Context ctx);
}
interface Filter {
Handler apply(Handler next);
default Filter then(Filter next) {
return h -> apply(next.apply(h));
}
default Handler then(Handler next) {
return ctx -> apply(next).apply(ctx);
}
}
interface Router {
Handler match(String method, String path);
//.. usual router methods
}
examples
HelloWorld
{
get("/", ctx -> "Hello world!");
}
Nothing new, except we replaced the (Request, Response)
signature by (Context)
Filter
{
/** Timing filter: */
filter(next -> {
return ctx -> {
long start = System.currentTimeMillis();
Object response = next.apply(ctx);
long end = System.currentTimeMillis();
System.out.println("Took: " + (end - start));
return response;
};
});
get("/", ctx -> "Hello world!");
}
Here the timing filter applies to all the route defined bellow it.
Filters are not tied to a path pattern
anymore, the Route.Chain
was replaced by a function composition.
Scoped Filter
{
group(() -> {
/** JWT auth filter: */
filter(new JWTToken());
get("/api/pets", ctx -> ...);
});
}
Here the JWT filter applies to all the route defined below which are wrapped by group operator.
unit tests
As expected unit test will be easily to write due we use a functional approach:
@Test
public void easyTest() {
Context ctx = mock();
Router router = ...;
Handler handler = router.match("/");
Object response = handler.apply(ctx);
assertEquals("Hello world!", response);
}
path pattern
We are going to remove the greedy Ant path pattern, so patterns like:
won't work any more
The new router algorithm will be a port of go-chi
thread model
We are going to keep the worker thread as default execution mode, also going to allow to execute a route in the IO thread:
{
mode(Mode.IO); // Default is WORKER
worker(ForkJoinPool.commonPool()); // Set a default worker or Fallback to `server` worker executor
get("/", ctx -> "from IO thread");
dispatch(() -> {
get("/default-worker", ctx -> "from worker thread"); // Run with default worker executor
});
dispatch(executor, () -> {
get("/executor", ctx -> "from custom executor thread"); // Run with a custom executor
});
}
Filters are going run properly (not like in 1.x where they are ignored #996)
Just a quick preview of what is coming in 2.x
As always, your feedback is welcome.
help wanted feedback