Kotlin + Helidon

Dmitry Aleksandrov
Helidon
Published in
4 min readMar 15, 2021

Let me tell you about my first experience using Kotlin with Helidon. (Trust me, its a good story!)

I was writing code and wanted to refactor it in some way. I looked at some of the menus in Intellij Idea, and I saw an option to “Convert to Kotlin” or I could use the key combination “⌥⇧⌘K” (Ctrl+Alt+Shift+K). And I love Kotlin. Hmm…

I was curious, so I switched to our Helidon SE Quickstart project, pressed the key combination mentioned above, and voilà! all of my code automatically converted to Kotlin. Then, to test the code, I went directly in the IDE and pressed the start button - and it worked! I really liked Kotlin and how easy it was to use, but I needed to try it with some other code. I decided to try it with the Helidon Quickstart project.

If you’re new to Helidon, let me give you a quick overview. Helidon has two frameworks: Helidon MP, our MicroProfile implementation, is our preferred way of creating microservices; and Helidon SE, which is our pure reactive Java set of APIs (which provides the backing for MP). We like to call SE “The Danger Zone”, since it provides you ultimate performance with no compromises. Now on to the testing.

I opened the Helidon Example project and started rewriting the examples in Kotlin. Converting the code automatically is a wonderful feature, but it still does not give you something people call “Idiomatic Kotlin.” You’ll still need to perform some manual configuration, but its worth it.

Let us do it!

So, how do you actually use Helidon MP with Kotlin? First, we add some dependencies to our pom:

<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

Next, we add kotlin-compiler-plugin to our pom build section:

<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmTarget>11</jvmTarget>
</configuration>
</plugin>

And that’s it!

Helidon MP

Now let’s look at our MP Quickstarter:

The first thing you should pay attention to is the use of open keyword. By default, Kotlin makes classes final, and that is not what we want to use with CDI. So one option is to explicitly mark classes and functions open, or use the Kotlin All Open plugin. I personally prefer the explicit marking.

Next are the repeatable annotations. Since Kotlin does not support them at this point, there is still a way to use them. For example, instead of:

TimeOfDay(from = "08:15:00", to = "12:00:00")
TimeOfDay(from = "12:30:00", to = "17:30:00")

We should use them together with their wrapper classes:

@TimeValidator.TimesOfDay(
TimeValidator.TimeOfDay(from = "08:15:00", to = "12:00:00"),
TimeValidator.TimeOfDay(from = "12:30:00", to = "17:30:00")
)

Basically, this is it for the Kotlin Helidon MP usage. Don’t forget about val / var usage with @Inject and use lateinit where required.

Now let’s try it with Helidon SE — this is where it gets interesting!

Helidon SE

Since Helidon SE is just pure Java with “no magic”, converting it should be even more straightforward. But there were some interesting use cases.

First is the usage of the keyword as. As you know, this is not a keyword in Java, and we widely use functions named as in our APIs. But in Kotlin I wanted to hide them. This is actually why the Helidon Kotlin Support project was started.

In order to “hide” the usage of the as keyword, we used the Kotlin — extensions feature.

So, instead of:

request.content()
.`as`(Employee::class)

you may use:

request.content()
.asSingle(Employee::class)

There is a big set of as replacements available.

Next way to “Kotlinize” our Helidon support is to add some “sugar”. In Helidon SE we widely use builder pattern to create all our objects, to guarantee correct object creation. This nicely fits the idea of creating small DSL.

So, instead of :

WebServer server = WebServer.builder(createRouting(config))
.config(config.get("server"))
.addMediaSupport(JsonpSupport.create())
.build();

We may use the “sugar”:

val server = webServer {
routing(createRouting(config))
config(config["server"])
addMediaSupport(jsonpSupport{})
}

Most of the objects that use builder pattern now have this DSL support.

Now if we put this all together, this is how our SE Quickstarter will look in Kotlin:

You may notice there is no main class, Kotlin will create it for us! This code may be run directly from your IDE, since it is pure Kotlin. And, by the way, all the example codebase shrunk. Another added benefit: less code!

So, to summarize…

We believe, that Helidon may nicely fit in the Kotlin server-side world. Especially if MicroProfile support and portability is required. Then both Helidon flavors fit nicely with Kotlin.

Just using the automatic conversion will work out of the box, but to make it really “Idiomatic Kotlin”, with as replacements and “Builder sugar”, we recommend you to try using the Helidon Kotlin Support project

We have prepared a huge set of examples, which you can find here:

Practically all of the usages of Helidon are now demonstrated and written in Kotlin!

More than that…

We are happy that our integration was noticed by JetBrains. Kotlin team experts gave some suggestions and provided some help with the examples, and, as a result, a special joint webinar was held, where you can find an extended description of Helidon+Kotlin support:

Huge thanks to Anton Arhipov for being such a wonderful host!

NOTE: This support is still considered EXPERIMENTAL, there are still many things to be done, but since Helidon is an open source project, we would appreciate it if you would submit your issues and PRs to help us improve. Thanks!

--

--