Back to Posts

Serverless Kotlin

Posted in Programming

Right now there is a lot of traction on the Serverless architecture so I decide to give a try and do some examples using Kotlin.

The full source of this post it’s available in this repository.

First I recommend to read this article by Mike Roberts from Martin Fowler website to get a bit more understanding on what is a Serverless architecture.

Them we should take a loot to the System Overview of Apache Openwhisk.

So now let starting to preparing 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 that the installation work 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 calculate a set of Fibonacci numbers, let try it out:

  fun main(args: Array<String>) = fibonacci(5).forEach(::println)

This should output:

  1
  1
  2
  3
  5

But how we could convert this into a Serverless function?

First we need to create a main function as OpenWhisk will be able to understand :

  fun main(args: JsonObject) : JsonObject {

  }

OpenWhisk use Google GSon so we will use in the final project maven to package our jar with dependencies, but for now let’s concentrate in the code, but the pom could be see here.

OpenWhisk will sent to our function as many parameters as we define when we create our function, so for this I’ll need a parameters 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 look OK but I think we could improve 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 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 latter we will use vagrant is better that we put our project in the OpenWhisk folder created at the beginning of the post since it will be map into the OpenWhisk machine.

I’ve create mine under openwhisk/projects/KotlinServerless and I’ll use maven wrapper

  $ cd openwhisk
  $ cd projects/KotlinServerless
  $ mvnw package

Now 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 an static method in Kotlin get 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 running 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 your done for the day, if not the OpenWhisk server sometimes get in 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.

About Juan Medina
I'm just a normal geek that code all kind of stuff, from complex corporate applications to games.

Games, music, movies and traveling are my escape pods.

Read Next

Modern C++ CI