Spring Fu Test Project: Link Unfurling Service

3 minute read

Every programmer occasionally, when nobody’s home, turns off the lights, pours a glass of scotch, puts on some light German electronica, and opens up a file on their computer. It’s a different file for every programmer. Sometimes they wrote it, sometimes they found it and knew they had to save it. They read over the lines, and weep at their beauty, then the tears turn bitter as they remember the rest of the files and the inevitable collapse of all that is good and true in the world. From Programming Sucks

Spring Fu

I have been messing around with a new Spring project called Spring Fu. Pivotal has a nice introduction video for the project. The key takeaways about Spring Fu are:

  • Its an experimental test bed for new Spring Boot features. The project is currently pre 0.0.1 so many things are still in flux.
  • Takes Spring 5 as a base (See Whats new in Spring 5 if you haven’t looked yet at what has changed)
  • The project is designed to be a lightweight micro-framework with low resource usage and fast startup.
  • Its written in Kotlin, the language made by JetBrains which has some nice improvements over Java.
  • Provides a DSL leveraging Functional Configuration which helps reduce the amount of reflection used.
  • Built off of Webflux for reactive programming and there is work in progress to adapt this to work with Kotlin’s coroutines.

The project builds off the minimal reactive example from the github project. I had briefly touched Kotlin and Gradle prior to this project when on an Android project but this was my first time working with the Kotlin Gradle DSL.

As always, the code is available for viewing.

I wanted a small and focused idea to test Spring Fu with. I decided to create a simple API for unfurling links.

The high level overview of the unfurling logic is:

  1. Fetch the requested page. This is the only step that involves I/O and it should be done reactively so its non blocking.
  2. If metadata in the head section contains tags for OpenGraph or Twitter Cards use that information
  3. Otherwise use default unfurler which uses the first paragraph as the description and the first image as the thumbnail
  4. Return JSON response containing title, thumbnail, and description

For example if you requested to unfurl “http://www.baeldung.com/spring-reactor” you would get back the following response: In this case there was OpenGraph meta data that was used to build the response.

The application configuration ended up very similar to the sample configuration for the project except that I configure beans specific to my handler and I also add Jackson as a codec since I’m producing a JSON response:

This sample project is small so there is only one API endpoint I have setup “/unfurl/{url}”. The sample code had inlined the routing logic into the main configuration but I chose to separate it like you would in a larger project. Additionally the routes do not sit inside a class since Kotlin allows top level methods.

Handlers in Webflux take a ServerRequest which contains all the request information and the handlers generate a Mono<ServerResponse>. Internally the handler is generating a Mono of the fetched document using Spring Webclient which is the reactive equivalent of the traditional RestTemplate. (As a side note, if I had needed to call another API rather than just fetch a page I would be tempted to use Feign. It seems like there is current work in progress to create a reactive version of Feign which I definitely would like to try out at some point in the future). Finally I’ve omitted the logic for extracting the document information since it is not Spring Fu specific.

Interacting with the developers behind the project

As I had mentioned Spring Fu is still very new and there were some bugs I encountered but the Kotlin Slack was super helpful for resolving through them:

  • I couldn’t import webflux-jackson to handle configuring json decoding/encoding. I got a response to my question in the #spring channel and then a swift fix in the project repository.
  • There was a bug around having a separate class for handling routing and it seems its due to a recent refactor, but I got a response and a fix was made.

Overall I enjoyed the experience and am excited to try out another project once 0.0.1 lands!