Javalin goes async

We’ve been working on async on and off for a long time. We’ve always ended up postponing it due to its trickiness, but we finally decided to sit down and spend some time on it. The API has been through several iterations, and we’ve landed on something that is both simple enough for end-users to use, and for us to implement (it’s also backwards compatible!).

So how does it work?

Just set a CompletableFuture<String> or CompletableFuture<InputStream> as your result:

import io.javalin.Javalin

fun main(args: Array<String>) {
    val app = Javalin.start(7000)
    app.get("/") { ctx -> ctx.result(getFuture()) }

// hopefully your future is less pointless than this:
private fun getFuture() = CompletableFuture<String>().apply {
    Executors.newSingleThreadScheduledExecutor().schedule({ this.complete("Hello World!") }, 1, TimeUnit.SECONDS)

You can only set future results in endpoint handlers (get/post/put/etc).
After-handlers, exception-handlers and error-handlers run like you’d expect them to after the future has been resolved or rejected.

A lot has changed behind the scenes, but everything is backwards compatible, so existing users who don’t care about asynchronicity shouldn’t be affected at all.

Example project

We made a small example project for illustrating the effects of using futures:

Please note, the tool is only intended for illustration. Use a proper benchmarking tool (like wrk) if you want to actually measure performance.

Performance improvements

Path matching now uses an EnumMap to split the search through the different http-methods (get/post/put/etc). This increases performance slightly for apps with a lot of routes.