Serverless Kotlin tutorial — You get the best of both worlds

© Shutterstock / nanami7
What’s better than Kotlin? Serverless, perhaps? If you can’t decide between the two, here’s a tutorial for you.
Right now there is a lot of traction on the Serverless architecture so I decided to give a try and do some examples using Kotlin. The full source of this post is available in this repository.
First, I recommend reading this article by Mike Roberts from Martin Fowler’s website to get a bit more understanding on what is a Serverless architecture. Then we should take a look at the System Overview of Apache OpenWhisk.
So now let’s prepare our platform. We will require Vagrant and Virtual Box, so install those first.
$ git clone --depth=1 https://github.com/apache/incubator-openwhisk.git openwhisk $ cd openwhisk/tools/vagrant $ ./hello
This will take a considerable amount of time so wait until everything is completed.
Now, to test if the installation works, we could just:
$ vagrant ssh -- wsk action invoke /whisk.system/utils/echo -p message hello --result
This will output:
{ "message": "hello" }
Now we could stop our Whisk server anytime with:
$ vagrant suspend
And bring it back with:
$ vagrant up
Now let’s create a simple Kotlin function:
fun fibonacci(numbers: Long): Array<Long> { if (numbers == 0L) return arrayOf(0L) if (numbers == 1L) return arrayOf(1L) var previous = 1L var current = 1L var temp: Long return arrayOf(1L, 1L) + (1..(numbers - 2)).map { temp = current + previous previous = current current = temp current }.toList().toTypedArray() }
This function just calculates a set of Fibonacci numbers, let’s try it out:
fun main(args: Array<String>) = fibonacci(5).forEach(::println)
This should output:
1 1 2 3 5
But how can we convert this into a Serverless function?
SEE ALSO: The Serverless Framework introduces OpenWhisk support
First, we need to create a main function as OpenWhisk will be able to understand :
fun main(args: JsonObject) : JsonObject { }
OpenWhisk uses Google GSon so we will use Maven in the final project to package our jar with dependencies, but for now, let’s concentrate on the code, but the pom could be seen here.
OpenWhisk will send [to our function] as many parameters as we define when we create our function, so for this, I’ll need a parameter called numbers.
We could get the value with a simple extension function:
fun JsonObject.paramOrElse(name: String, elseValue: Long): Long = if (this.has(name)) this.getAsJsonPrimitive(name).asLong else elseValue
So now in our main function, we could do something like :
fun main(args: JsonObject) : JsonObject { val numbers = args.paramOrElse("numbers", 10L) val results = fibonacci(numbers) }
All so now we need to get a result so we could do something like:
fun main(args: JsonObject) : JsonObject { val numbers = args.paramOrElse("numbers", 10L) val results = fibonacci(numbers) val response = JsonObject() val elements = JsonArray() results.forEach{ elements.add(it) } response.add("result", elements) return response }
This looks OK but I think we could improve it by adding just one extension function:
fun Long.throughFunction(operation: (Long) -> Array<Long>): JsonObject { val result = JsonObject() val elements = JsonArray() operation(this).forEach(elements::add) result.add("result", elements) return result }
With this our main could become just:
fun main(args: JsonObject) = args.paramOrElse("numbers", 10L).throughFunction(::fibonacci)
Let’s put all the parts together :
package org.learning.by.example.serverless.kotlin import com.google.gson.JsonArray import com.google.gson.JsonObject fun fibonacci(numbers: Long): Array<Long> { if (numbers == 0L) return arrayOf(0L) if (numbers == 1L) return arrayOf(1L) var previous = 1L var current = 1L var temp: Long return arrayOf(1L, 1L) + (1..(numbers - 2)).map { temp = current + previous previous = current current = temp current }.toList().toTypedArray() } fun JsonObject.paramOrElse(name: String, elseValue: Long): Long = if (this.has(name)) this.getAsJsonPrimitive(name).asLong else elseValue fun Long.throughFunction(operation: (Long) -> Array<Long>): JsonObject { val result = JsonObject() val elements = JsonArray() operation(this).forEach(elements::add) result.add("result", elements) return result } fun main(args: JsonObject) = args.paramOrElse("numbers", 10L).throughFunction(::fibonacci)
With this prototype we could create many functions, in fact with a couple of generics could get even better but let’s do that another day.
So now we need to compile our function since later we will use Vagrant — it is better that we put our project in the OpenWhisk folder created in the beginning of the post since it will be map into the OpenWhisk machine.
I’ve created mine under openwhisk/projects/KotlinServerless and I’ll use Maven wrapper
$ cd openwhisk $ cd projects/KotlinServerless $ mvnw package
Now let’s get back into the Vagrant directory and ssh into the OpenWhisk machine:
$ cd openwhisk $ cd tools/vagrant $ vagrant ssh
Now from the OpenWhisk machine, we will get into our function directory:
$ cd openwhisk/projects/KotlinServerless/target $ wsk action create Fibonacci KotlinServerless-1.0-SNAPSHOT-jar-with-dependencies.jar --main \ org.learning.by.example.serverless.kotlin.FibonacciKt ok: created action Fibonacci
We need to specify the full location of our class, and remember than a static method in Kotlin gets created in a class named _FileName_Kt.class
Now let’s run our function:
$ wsk action invoke --result Fibonacci --param numbers 5
And we will get as output something like:
{ "result": [ 1, 1, 2, 3, 5 ] }
And of course, we could run without parameters as:
$ wsk action invoke --result Fibonacci
And the result will be:
{ "result": [ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ] }
Finally, don’t forget to suspend the Vagrant machine after you’re done for the day, if not the OpenWhisk server sometimes gets in a bad state.
$ vagrant suspend
You could get it back simply with:
$ vagrant up
I think this is enough for today, let’s see what else we do another day with this interesting topic.
This post was originally published on Juan Medina’s blog.
Don’t miss the Serverless Cloud series with Bart Blommaerts, software architect at Ordina:
- An introduction to the serverless architectural style
- FaaS providers: Auth0 Webtask, Google Cloud Functions, Azure Functions & IBM Bluemix OpenWhisk
- The serverless cloud — Enabler of a collaborative economy
asap