Kotlin coroutines and Spring 5

This article is a next one in the series which is focused on practical application of Kotlin experimental language feature - coroutines. This example shows how to use coroutines with Spring 5 Reactive Web Framework to write linear, concurrent and non-blocking code for Spring web applications.

It is highly recommended to review intro to coroutines from this blog and Guide to kotlinx.coroutines by example from JetBrains before reading this article. Also it is expected that you are familiar with basics of Spring (including Spring Boot), although this is not required.

Quick intro

As we found out previously, coroutines is the powerful mechanism which allows to write concurrent, non-blocking code without loosing in readability and keeping the source code “sequential”. Kotlin coroutines are nicely integrated with existing non-blocking frameworks from Java ecosystem, like RxJava and Reactive Streams (which is planned to be a part of upcoming Java 9).

Spring 5 is the next major version of Spring Framework, which introduces a whole lot of improvements and new features. One of them which we are mostly interested at the moment is Reactive Web Framework, or WebFlux. It is based on implementation of Reactive Streams API by the project called Project Reactor.

And it turns out, that Kotlin and Spring WebFlux are playing very well together. Kotlin coroutines can be smoothly integrated with reactive types and Spring WebFlux uses that API for non-blocking I/O.

Reactive types

Let’s start with a short introduction to summarize the knowledge of reactive types in Spring 5 you got from the official intro. We should start with Reactive Streams library which introduces fundamental class to describe publisher/subscriber interaction - Publisher<T> which is very similar to Observable from RxJs and RxJava. This is a very basic interface which only defines one operation - subscription and can provide potentially unbounded number of elements to its consumers . Project Reactor adds two implementations of Publisher - Mono which implies a single value and Flux - stream of values. Using any of these types as a return value of a function assumes that function will return immediately (without blocking current thread) and the result (Flux or Mono) will be available to subscribers later.

Important to note that Flux doesn’t mean infinite stream, as you might expect from generator. It could be a finite sequence of elements with values published to the subscribers on arrival.

Blocking code

Let’s talk about Spring now. To start with an example, consider the following controller method. Class declaration and application code are omitted, please see the full source code if you’d like to.

@GetMapping( "/blocking/{personId}" ) fun getMessagesFor (@PathVariable personId: String): String { val person = peopleRepository .findById(personId) .orElseThrow { NoSuchElementException( "Not found" ) } val lastLogin = auditRepository .findByEmail(person.email).eventDate val numberOfMessages = messageRepository .countByMessageDateGreaterThanAndEmail(lastLogin, person.email) return "Hello ${person.name}, you have $numberOfMessages messages since $lastLogin" }

This controller method takes personId as a parameter, finds a person with given ID in Mongo repository, or throws NoSuchElementException . Then, method used person’s email to find a last login date in the audit repository. Finally, number of received messages is fetched from the third repository.

This is a classic blocking code, every time we call a method on a repository object, the execution thread is blocked until the result is received. To make it concurrent we can easily switch it to WebFlux and utilize reactive types we’ve been discussing earlier.

Make it reactive

To do this, we just need to change return type of the controller method to be one of the reactive types ( Mono or Flux ,) and Spring will take care about the rest. However due to non-linear nature of execution of reactive code we need to introduce a whole lot of callbacks within map or flatMap function. Anyway, let’s try and see how reactive method may look like.

@GetMapping( "/reactive/{personId}" ) fun getMessagesFor (@PathVariable personId: String): Mono<String>? { return peopleRepository.findById(personId) .switchIfEmpty(Mono.error(NoSuchElementException())) .flatMap { person -> auditRepository.findByEmail(person.email) .flatMap { lastLogin -> messageRepository.countByMessageDateGreaterThanAndEmail(lastLogin.eventDate, person.email) .map { numberOfMessages -> "Hello ${person.name}, you have $numberOfMessages messages since ${lastLogin.eventDate}" } } } }

Important to note that repositories we are using are not classic CrudRepository , but ReactiveCrudRepository instances. As you can guess instead of returning Person from findById method, reactive repository returns Mono<Person> hence map and flatMap methods.

switchIfEmpty here is just to handle non-existing person and throw NoSuchElementException when reactive repository returns empty Mono (i.e. no values).

The code above does all the same as the first version. So now we have non-blocking code however we lose in readability and need to deal with callback chained via map and flatMap methods combination.

Kotlinize it!

You might think - it would be nice to keep non-blocking reactive nature of our code, but keep it linear and readable as in classic example. Well, thanks to Kotlin coroutines it is possible.

Let’s take a look at the code and then discuss what have changed.

@GetMapping( "/coroutine/{personId}" ) fun getMessages (@PathVariable personId: String) = mono(Unconfined) { val person = peopleRepository .findById(personId).awaitFirst() val lastLogin = auditRepository .findByEmail(person.email).awaitFirst().eventDate val numberOfMessages = messageRepository.countByMessageDateGreaterThanAndEmail(lastLogin, person.email).awaitFirst() val message = "Hello ${person.name}, you have $numberOfMessages messages since $lastLogin" message }

Noticed differences with the first example? Our function now wrapped into mono(Unconfined) method call. This is our gateway from Reactor type ( Mono ) to Kotlin’s coroutines. The mono(...) function is a coroutine builder, similar to launch and async we’ve seen in Kotlinx coroutine examples. As any coroutine builder it specifies context to use. Unconfined means coroutine will start in the calling thread but then can be multiplexed by a scheduler to other threads as well. The function also returns a value, although it doesn’t have return keyword, because the whole function is now lambda and Kotlin doesn’t need return in lambdas. Thanks to type inference, Kotlin also manages to infer return type based on type of the return value. So signature of our function is actually this:

fun getMessages (@PathVariable personId: String): Mono<String>

So now we have a coroutine function that returns a Mono<String> and more importantly is we can now call suspendable functions from our controller method. Remember the functions that do no block the current thread? That is it!

Current version of kotlinx.coroutines.reactor is not yet compatible with latest release of Project Reactor (3.1 Bismuth M1) which is required by Spring Data Reactive Mongo. So we cannot use official build from JetBrains. Instead, sample project includes custom build of kotlinx.coroutines.reactor updated to Project Reactor 3.1. Please note, this version is not intended for anything more than experimenting in the scope of this article!

Right, so now we can call suspendable function but we still need to somehow turn Mono<Person> (returned by our reactive repository) to a such function. Luckily there are a few extension functions for a base Publisher<T> class (from Reactive Streams) in kotlinx.coroutinex.reactive package which extend any Publisher and thus Mono and Flux values with a set of new methods called await* . In our case we are using awaitSingle() which yields control over current thread back to scheduler until target Publisher returns a value.

And that is pretty much it, having a combination of mono method to produce a reactive type and awaitSingle to consume it in non-blocking manner we can build complex non-blocking function and keep code clean and linear.

Conclusion

To use reactive types which are building blocks of Spring 5 reactive web framework (WebFlux) with Kotlin coroutines there is a set of functions provided by kotlinx.coroutines.reactive/reactor libraries. These functions help to easily build (using mono / flux coroutine builders) a reactive value and also subscribe to a reactive result in non-blocking manner using await* extension functions.

Disclaimer

Coroutines is still an experimental feature of Kotlin and cannot be used in production code. Versions of Spring (RC1), Spring Boot (M1) and Project Reactor (Bismuth M1) are also not finalized yet, please follow the announcements from Spring team.