Decisions, decisions

Serverless Kotlin tutorial — You get the best of both worlds

Juan Antonio Medina

© 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:

asap

Author

Juan Antonio Medina

Juan Antonio Medina is just a normal geek that codes all kind of stuff, from complex corporate applications to games. Games, music, movies and traveling are his escape pods. Find him on Twitter at @jamedinatwit 


Comments
comments powered by Disqus