For a long time, Kotlin was on my list of things I wanted to learn more about. In the second half of 2022, I finally got the chance to learn the language and started using Ktor, the ‘web application runtime’ for Kotlin.
Kotlin
Kotlin is a modern programming language that offers several benefits over Java. Firstly, Kotlin supports object-oriented, functional, and procedural programming constructs, making it a versatile language for a wide range of projects.
Secondly, Kotlin is fully interoperable with Java, which means that developers can easily use Kotlin in conjunction with existing Java codebases. Additionally, Kotlin offers support for several concepts, such as data classes, structured concurrency, and virtual threads, which were not available in Java until much later. Kotlin also offers null safety features, which help prevent common errors that can arise due to null pointer exceptions.
Another significant benefit of Kotlin is its compact and highly readable grammar, which makes code more concise and easier to understand. Finally, Kotlin offers extension functions, which allow developers to add new functions to existing classes, making it easier to write reusable code.
Kotlin is a modern, powerful, and highly expressive programming language that helps developers write better code faster and more efficiently.
We will encounter various powerful constructs of Kotlin in this blog series called “project FF”. In this Framework Face-off, I will compare several Java application runtimes, including Spring Boot and Quarkus, with Jakarta EE and how you implement Enterprise functionality.
But today, I give already two examples of the power of Kotlin.
Kotlin extension functions
Kotlin extension functions allow you to add new functions to an existing class without having to inherit from that class or modify its source code.
In other words, extension functions let you extend the functionality of a class in a more flexible and modular way. This can make your code more readable and easier to maintain.
Extension functions are similar to static utility methods, but with the added benefit of being able to call them as if they were instance methods of the class, they are extending. This can make your code more concise and easier to understand.
fun String?.hasSpaces(): Boolean { val found = this?.find { it == ' '} return found != null } fun main() { val data = "A sentence with spaces in of course" println(data.hasSpaces()) // True val data2 = "word" println(data2.hasSpaces()) // False }
For a detailed explanation of this code, I refer you to the Kotlin tutorials. In short, the hasSpaces function is defined on a nullable String type. The function body checks if we find a space in the string value. From that point on, we can use the hasSpaces on a String as you can see in the main function.
Creating an interceptor
You can create an interceptor in plain Kotlin using lambdas and high-order functions. Functions and lambdas are first-class citizens and are treated equally as data objects.
inline fun timeBlock(block: () -> T): Pair { val startTime = System.nanoTime() val result = block() // execute lambda val endTime = System.nanoTime() return Pair(result, endTime - startTime) } fun main() { val number = 121 val (isPrimeValue, time) = timeBlock { (2..number/2).none { number % it == 0 } } println("Result of prime check for $number is $isPrimeValue ") println("Execution took $time nanoSeconds ") }
Here we define a function that accepts a lambda and we time the execution of the lambda. The function returns the result of the lambda and the time it took.
As example, we time the calculation to check if a value is a prime value. Also here you can see the power of Kotlin but still keep it readable.
(2..number/2).none { number % it == 0 }
defines that we go from value 2 until the half of the value we need to check and there should be no value where the modulo of the value with the number is 0.
Ktor
Ktor is a lightweight, asynchronous web server that easily allows you to define what is needed. In the blog “Time is code, My friend”, Ktor performed really well and performed as good as Quarkus which has a very elaborate and complex pre-processing system to achieve such a fast startup.
Since it is highly modular, you can keep the runtime, and memory usage very low. And since there are modules available for each major topic or framework, you can create applications that integrate with them very easily.
Also, Ktor is created with Kotlin Coroutines support built-in. Kotlin Coroutines provide you with solutions for asynchronous non-blocking programming, structured concurrency and solutions similar to Java Virtual Threads. Things that will only appear in Java in the coming versions but are already available for many years within Kotlin.
Ktor Example
In this blog, I’ll show you how you can create an application with endpoints that handle JSON payload.
You can generate a Maven or Gradle project using the Ktor Project Generator web page or directly from within IntelliJ IDEA.
By default, the Netty engine is selected to handle the HTTP request handling but you can select other engines if you like.
Under the plugin section, make sure you select the ‘kotlinx.serialization‘ plugin to have support for JSON. This automatically brings in the routing plugin which is responsible for calling a certain Kotlin function for a URL pattern and the Content Negotiation plugin that sets header values accordingly to the requirements like JSON types.
The generated project contains a main function within the Application.kt file which starts Netty and an extension function that define the configuration of the modules. This allows for a clear separation of each functionality and quickly lets you find each setting.
To have full JSON support, we don’t need to configure anything, just activate the plugin. The Maven plugin in this case makes sure that any class that is annotated with @Serializable
can be used at runtime without the need for any reflection. This makes the solution native compile friendly. This a topic we will discuss later in more detail.
For the routing, we call a function that corresponds to the HTTP method we want to support. It has a URL pattern and lambda expression as a parameter. This way, we can define the function for each URL in a very concise and readable way.
get("/") { call.respondText("Hello World!") }
The entire example can be found at the Project FF Github repository: https://github.com/rdebusscher/Project_FF/tree/main/ktor-started/JAX-RS
Conclusion
In a time where modular runtimes that are small and take up little memory are important for some people, Kotlin and Ktor are ideal solutions. Kotlin offers many benefits over Java and offers a compact syntax but still highly readable. It supports many important features already for years that are introduced only recently or planned in Java.
Ktor is the natural solution if you choose Kotlin and need to write a backend application. Although you can combine Kotlin with Spring Boot and Quarkus, Ktor is a lightweight solution that has the same functionality available through its modules and is faster at runtime.
Training and Support
Do you need a specific training session on Kotlin, Jakarta EE or MicroProfile? Or do you need some help in getting up and running with your next Ktor project? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.