Easy lift off with Kotlin and PCF

For me, Kotlin, Spring Boot/Cloud and Pivotal Cloud Foundry (PCF) share a key thing in common – they make it so easy to get ideas into working, robust software as quickly as possible. PCF, like Spring Boot, takes an opinionated approach that looks after a lot of the heavy lifting required to get a suite of microservices into production along with providing monitoring capabilities. It abstracts the underlying cloud infrastructures such as AWS that it can deploy services to. At the same time though, it is possible to go under the hood in order to perform more custom operations tasks and configurations.

For a quick peak at this, lets write a microservice to handle employee expenses.

Prerequistes (all free!)

Creating a Kotlin Spring Boot Service to manage employee expenses

To get going, we need to create a Spring Boot project with the correct dependencies to create a Restful microservice. Luckily this is straight forward using Spring Initializer at:

Select the options as in the screenshot below: Maven, Kotlin and Spring Web for the dependencies.

We’ll give the project a group id of com.somecompany.financials and an artifact id of expenses-service After the Spring Web dependency is chosen, you should see the following:

Click "Generate" and "expenses-service.zip" will be downloaded.

Extract this to a directory of your choice and open in Intellij and the project will be imported automatically as a maven project. The Spring Initializer will have bootstrapped in the necessary dependencies and the service can be started by navigating to the ExpensesServiceApplication.kt file and simply executing the main method. This will start up an embedded tomcat server on port 8080.

This doesn’t do too much at the moment so lets just add a very simple endpoint which will simply return all expenses. One of the beauties of Kotlin is the ability to have multiple top level classes in the same file as opposed to having to create separate class files for each public class in a package. This is especially handy for trying out ideas quickly. As an application grows in size though, it can be easier to separate out these classes into separate files for better organization. All of the code for the rest of this blog post will simply be added in the ExpensesServiceApplication.kt file. Also, for now, we will just store expenses in memory.

The Expense is modelled as an ADT (Algebraic Data Type). I wrote about the advantages of ADTs in a previous post. It’s essentially specifying the exact types of expenses that are possible in our domain model. Later when using Kotlin’s when expression to perform an action on an Expense, the Kotlin compiler is kind enough to let us know if we forget to take care of one or more types of Expenses.

sealed class Expense {}
data class Meals(val employeeId: UUID, val submissionDate: ZonedDateTime, val amount: BigDecimal) : Expense()
data class Travel(val employeeId: UUID, val submissionDate: ZonedDateTime, val amount: BigDecimal, val origin: String, val destination: String, val mode: Mode) : Expense()
data class Fuel(val employeeId: UUID, val submissionDate: ZonedDateTime, val amount: BigDecimal, val distanceInKM: Int) : Expense()

enum class Mode() {
   Air, Rail, Sea, Taxi, RentedCar
}

As a side note, in the above code, certain properties are duplicated across the subtypes of Expense e.g. submissionDate.
I usually favour keeping the sealed class supertype as minimal as possible containing little or no properties. It means that the subtypes are kept as flexible as possible. I would see the type of Expense as a "type constructor" (with no type parameter in this case) and each subtype – e.g. Meals – as a "data term value".

In order to create an endpoint to return expenses, we can create a Spring Controller as follows: (again this is added directly in ExpensesServiceApplication.kt)

@RestController
@RequestMapping("/expenses")
class ExpenseController

In order to have expenses to return from our endpoint, lets just create an in-memory collection and lets keep it thread safe as we may want to add other endpoints later. A ConcurrentLinkedQueue will do the job for now. Lets add this to our Controller:

private val expenses = ConcurrentLinkedQueue<Expense>(
   setOf( Meals(  UUID.randomUUID(),
               ZonedDateTime.of(LocalDateTime.of(2020, 1, 15, 16, 30), ZoneOffset.UTC),
               23.toBigDecimal()),
         Travel(    UUID.randomUUID(),
               ZonedDateTime.of(LocalDateTime.of(2019, 12, 14, 13, 12), ZoneOffset.UTC),
               12.toBigDecimal(),
               "Cork",
               "London",
               Mode.Air),
         Fuel(  UUID.randomUUID(),
               ZonedDateTime.of(LocalDateTime.of(2019, 11, 19, 16, 30), ZoneOffset.UTC),
               45.toBigDecimal(),
               45),
         Travel(    UUID.randomUUID(),
               ZonedDateTime.of(LocalDateTime.of(2019, 12, 1, 9, 37), ZoneOffset.UTC),
               12.toBigDecimal(),
               "Limerick",
               "Dublin",
               Mode.Rail),
         Fuel(  UUID.randomUUID(),
               ZonedDateTime.of(LocalDateTime.of(2019, 2, 1, 16, 30), ZoneOffset.UTC),
               12.toBigDecimal(),
               45),
         Meals( UUID.randomUUID(),
               ZonedDateTime.of(LocalDateTime.of(2019, 9, 4, 21, 10), ZoneOffset.UTC),
               14.toBigDecimal())))

This takes advantage of the handy setOf function in kotlin to create a Set of Expenses initialized with a few expenses so that we have some thing to return from our GET endpoint.

We can separate our core Expense domain model from the data that we will be returning from our endpoint with a DTO structure that essentially mirrors the Expense type shown earlier. Similar to languages like Scala and F#, this is quite concise in Kotlin and doesn’t involve having to create separate class files for each of the subtypes. Its shown in the code below as well as a conversion function to convert from a core Expense to a DTO expense. This function takes advantage of the extension method mechanism in Kotlin so that later, the function can be called on an Expense object as if it were a method on the Expense class.

sealed class ExpenseDTO(val type: ExpenseType) {
   data class MealsDTO(val employeeId: UUID, val submissionDate: ZonedDateTime, val amount: BigDecimal) : ExpenseDTO(ExpenseType.Meals)
   data class TravelDTO(val employeeId: UUID, val submissionDate: ZonedDateTime, val amount: BigDecimal, val origin: String, val destination: String, val mode: Mode) : ExpenseDTO(ExpenseType.Travel)
   data class FuelDTO(val employeeId: UUID, val submissionDate: ZonedDateTime, val amount: BigDecimal, val distanceInKM: Int) : ExpenseDTO(ExpenseType.Fuel)
}

enum class ExpenseType {
   Meals, Travel, Fuel
}

private fun Expense.toDto() =
   when (this) {
      is Meals -> ExpenseDTO.MealsDTO(employeeId, submissionDate, amount)
      is Travel -> ExpenseDTO.TravelDTO(employeeId, submissionDate, amount, origin, destination, mode)
      is Fuel -> ExpenseDTO.FuelDTO(employeeId, submissionDate, amount, distanceInKM)
   }

Now we are ready to create an endpoint to return our in-memory expenses as follows:

@GetMapping("")
fun getExpenses() = expenses.toSet().map { it.toDto() }

Now, we can restart the app by running the main method on ExpensesServiceApplication.kt. We can call our endpoint to get all expenses in the browser, using curl, Postman or, if you’re using Intellij Ultimate, a http scratch file will do the job.

We can see our expenses getting returned in the response:

[
  {
    "employeeId": "42f5af92-d8aa-4e35-a23d-b0010d7a9b47",
    "submissionDate": "2020-01-15T16:30:00Z",
    "amount": 23,
    "type": "Meals"
  },
  {
    "employeeId": "9633a8f0-7dd0-4916-bff3-8a71ef6d3063",
    "submissionDate": "2019-12-14T13:12:00Z",
    "amount": 12,
    "origin": "Cork",
    "destination": "London",
    "mode": "Air",
    "type": "Travel"
  },
  {
    "employeeId": "7bbb8954-1642-4b9e-a530-a186cca1da9e",
    "submissionDate": "2019-11-19T16:30:00Z",
    "amount": 45,
    "distanceInKM": 45,
    "type": "Fuel"
  },
  {
    "employeeId": "78253ec9-309d-47af-a24a-87f9585cd5eb",
    "submissionDate": "2019-12-01T09:37:00Z",
    "amount": 12,
    "origin": "Limerick",
    "destination": "Dublin",
    "mode": "Rail",
    "type": "Travel"
  },
  {
    "employeeId": "afe82285-24be-4eeb-bc1d-6c7a3356e1a2",
    "submissionDate": "2019-02-01T16:30:00Z",
    "amount": 12,
    "distanceInKM": 45,
    "type": "Fuel"
  },
  {
    "employeeId": "58c7d770-a6de-4e76-ab18-f0e703dcf8b4",
    "submissionDate": "2019-09-04T21:10:00Z",
    "amount": 14,
    "type": "Meals"
  }
]

That’s it for the microservice logic. The great thing is, with Kotlin, this can all be simply coded in the ExpensesServiceApplication.kt file. For a tiny microservice this can be fine as it keeps things simple. If it gets more complex, Intellij refactoring tools make it a breeze to pull out parts of this code into other files. For completeness, the entire ExpensesServiceApplication.kt is below:

package com.somecompany.financials.expensesservice

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.math.BigDecimal
import java.time.*
import java.util.*
import java.util.concurrent.ConcurrentLinkedQueue

@SpringBootApplication
class ExpensesServiceApplication

fun main(args: Array<String>) {
   runApplication<ExpensesServiceApplication>(*args)
}

sealed class Expense {}
data class Meals(val employeeId: UUID, val submissionDate: ZonedDateTime, val amount: BigDecimal) : Expense()
data class Travel(val employeeId: UUID, val submissionDate: ZonedDateTime, val amount: BigDecimal, val origin: String, val destination: String, val mode: Mode) : Expense()
data class Fuel(val employeeId: UUID, val submissionDate: ZonedDateTime, val amount: BigDecimal, val distanceInKM: Int) : Expense()

enum class Mode() {
   Air, Rail, Sea, Taxi, RentedCar
}


@RestController
@RequestMapping("/expenses")
class ExpenseController {

private val expenses = ConcurrentLinkedQueue<Expense>(
   setOf( Meals(  UUID.randomUUID(),
               ZonedDateTime.of(LocalDateTime.of(2020, 1, 15, 16, 30), ZoneOffset.UTC),
               23.toBigDecimal()),
         Travel(    UUID.randomUUID(),
               ZonedDateTime.of(LocalDateTime.of(2019, 12, 14, 13, 12), ZoneOffset.UTC),
               12.toBigDecimal(),
               "Cork",
               "London",
               Mode.Air),
         Fuel(  UUID.randomUUID(),
               ZonedDateTime.of(LocalDateTime.of(2019, 11, 19, 16, 30), ZoneOffset.UTC),
               45.toBigDecimal(),
               45),
         Travel(    UUID.randomUUID(),
               ZonedDateTime.of(LocalDateTime.of(2019, 12, 1, 9, 37), ZoneOffset.UTC),
               12.toBigDecimal(),
               "Limerick",
               "Dublin",
               Mode.Rail),
         Fuel(  UUID.randomUUID(),
               ZonedDateTime.of(LocalDateTime.of(2019, 2, 1, 16, 30), ZoneOffset.UTC),
               12.toBigDecimal(),
               45),
         Meals( UUID.randomUUID(),
               ZonedDateTime.of(LocalDateTime.of(2019, 9, 4, 21, 10), ZoneOffset.UTC),
               14.toBigDecimal())))


   @GetMapping("")
   fun getExpenses() = expenses.toSet().map { it.toDto() }

   sealed class ExpenseDTO(val type: ExpenseType) {
      data class MealsDTO(val employeeId: UUID, val submissionDate: ZonedDateTime, val amount: BigDecimal) : ExpenseDTO(ExpenseType.Meals)
      data class TravelDTO(val employeeId: UUID, val submissionDate: ZonedDateTime, val amount: BigDecimal, val origin: String, val destination: String, val mode: Mode) : ExpenseDTO(ExpenseType.Travel)
      data class FuelDTO(val employeeId: UUID, val submissionDate: ZonedDateTime, val amount: BigDecimal, val distanceInKM: Int) : ExpenseDTO(ExpenseType.Fuel)
   }

   enum class ExpenseType {
      Meals, Travel, Fuel
   }

   private fun Expense.toDto() =
      when (this) {
         is Meals -> ExpenseDTO.MealsDTO(employeeId, submissionDate, amount)
         is Travel -> ExpenseDTO.TravelDTO(employeeId, submissionDate, amount, origin, destination, mode)
         is Fuel -> ExpenseDTO.FuelDTO(employeeId, submissionDate, amount, distanceInKM)
      }
}

Preparing for lift off

To get our expenses microservice into the cloud on PCF, we need to first create a PCF account. So first we need to go to PCF and set up an account. Click the sign up for free button underneath which at the time of writing this blog it specifies that $87 of free trial credit is given.

This takes us to a sign up page where we can enter details to create an account.

After filling in details and clicking "Sign Up", we are brought to a screen that lets us know that an account verification email has been sent to us. Once we check our email and verify, we can now sign into our PCF account by going to the PCF Login

After signing in, we are brought to a main screen

Click into "Pivotal Web Services" This brings us to a wizard to add a few more details to claim our free trial.

As part of going through this wizard, we can create an "Org" which is essentially an account in pcf for developers to collaborate and deploy services and computing resources etc. The org name can be changed later so for now, we can just call it demo-1000. This name needs to be unique, so if you are following along, "demo-" with some high number will do the trick.

This brings us a portal to manage our org.

We can see that there is one "space" within the Org called "development" by default. A space is an environment like "development", "qa" "production" etc. This is where we will be deploying our expenses-service to.

So far we have interacted with PCF through the online portal. However, going forward we will want to automate everything. PCF provides a command line interface called "cf" which can be downloaded from here:.

Lifting Off

With the cf command line interface installed, we can log into our pcf account with

cf login -a https://api.run.pivotal.io

After logging in, it will output something like below:

Authenticating...
OK

Targeted org demo-1000

Targeted space development


                
API endpoint:   https://api.run.pivotal.io (API version: 2.144.0)

Before we can push the expenses-service to the cloud we need to run mvn clean install in the root directory of the project to produce an executable jar containing our microservice. This produces expenses-service-0.0.1-SNAPSHOT.jar in the target directory.

We can now lift this off to the cloud by simply navigating to the target directory and running.

cf push

PCF uses what are called buildpacks to package and deploy apps. It is clever enough to know that we are deploying a JVM based microservice so in the output of the cf push command, we can see that it is using the java buildpack.

Creating app expenses-service...
Mapping routes...


   Downloaded app package (18.8M)
   -----&gt; Java Buildpack v4.26 (offline) | https://github.com/cloudfoundry/java-buildpack.git#e06e00b
   -----&gt; Downloading Jvmkill Agent 1.16.0_RELEASE from https://java-buildpack.cloudfoundry.org/jvmkill/bionic/x86_64/jvmkill-1.16.0-RELEASE.so (found in cache)
   -----&gt; Downloading Open Jdk JRE 1.8.0_232 from https://java-buildpack.cloudfoundry.org/openjdk/bionic/x86_64/openjdk-jre-1.8.0_232-bionic.tar.gz (found in cache)

Waiting for app to start...

name:              expenses-service
requested state:   started
routes:            expenses-service.cfapps.io
last uploaded:     Sun 12 Jan 11:01:21 GMT 2020
stack:             cflinuxfs3
buildpacks:        client-certificate-mapper=1.11.0_RELEASE container-security-provider=1.16.0_RELEASE
                   java-buildpack=v4.26-offline-https://github.com/cloudfoundry/java-buildpack.git#e06e00b java-main java-opts
                   java-security jvmkill-agent=1.16.0_RELEASE open-jdk...

type:            web
instances:       1/1
memory usage:    1024M

When it has finished deploying the app, we will see something like the following in the output:

     state     since                  cpu    memory         disk         details
#0   running   2020-01-12T11:01:54Z   0.0%   128.6M of 1G   130M of 1G

We can run the command

cf apps

to see some info about our running microservice. This will output:

name               requested state   instances   memory   disk   urls
expenses-service   started           1/1         1G       1G     expenses-service.cfapps.io

The url will vary on your machine and may contain a random postfix after "expenses-service". Now we can navigate to

http://expenses-service.cfapps.io/expenses

(or whatever url is displayed in the "urls" column above if you are following along)in the browser, or any http client, and see our expenses being returned!

GET http://expenses-service.cfapps.io/expenses

HTTP/1.1 200 OK
Content-Type: application/json
Date: Sun, 12 Jan 2020 11:12:47 GMT
X-Vcap-Request-Id: 7b9d0de2-e1fa-4599-4a55-a290b1718f6a
Content-Length: 872
Connection: keep-alive

[
  {
    "employeeId": "8c1416ed-54f5-4003-b422-d4b1e5fc92ae",
    "submissionDate": "2020-01-15T16:30:00Z",
    "amount": 23,
    "type": "Meals"
  },
  {
    "employeeId": "e0438bed-d35f-4d5a-a00c-900b29ba6a90",
    "submissionDate": "2019-12-14T13:12:00Z",
    "amount": 12,
    "origin": "Cork",
    "destination": "London",
    "mode": "Air",
    "type": "Travel"
  },
  {
    "employeeId": "a01235c8-e919-4a26-ad2b-617928b3aed4",
    "submissionDate": "2019-11-19T16:30:00Z",
    "amount": 45,
    "distanceInKM": 45,
    "type": "Fuel"
  },
  {
    "employeeId": "6021b0ce-6c3c-41ad-92dc-c00cc00bdc0f",
    "submissionDate": "2019-12-01T09:37:00Z",
    "amount": 12,
    "origin": "Limerick",
    "destination": "Dublin",
    "mode": "Rail",
    "type": "Travel"
  },
  {
    "employeeId": "b71e7f48-c8e6-4718-abef-338740548ef2",
    "submissionDate": "2019-02-01T16:30:00Z",
    "amount": 12,
    "distanceInKM": 45,
    "type": "Fuel"
  },
  {
    "employeeId": "6c24924c-3dd9-4c12-957d-eead504dfb32",
    "submissionDate": "2019-09-04T21:10:00Z",
    "amount": 14,
    "type": "Meals"
  }
]

In this post, hopefully I have shown the power of the Kotlin, Spring Boot and PCF combination. This is even more convenient and powerful when it comes to combining it with Spring Cloud and building, deploying and monitoring a whole suite of microservices. This blog post also allowed PCF to take its most opinionated approach to deploying our jar to the cloud. However, a lot more control and configuration is possible through using a manifest.yml file to better configure and automate deployment along with other mechanisms to gain more lower level control.

4 Replies to “Easy lift off with Kotlin and PCF”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s