05 December 2016

Simple REST Server With Kotlin


With micro services being the latest important development in Software Engineering anyone doing server side development needs to be involved in this area. One of the major areas where Kotlin is being used is in server side development, especially micro services. This post will be covering how to create a simple REST server using the following technologies:


  • Gradle (any 2.x version)
  • Kotlin v1.0.5-2 (stable version)
  • Grizzly v2.3.28 (embedded HTTP web server)
  • Grizzly Jersey Container v2.23.2 (JAX-RS implementation for Grizzly)
  • Jackson v2.23.2 (JSON library)
  • Linux PC (using Debian, Ubuntu or Linux Mint)

Setup Project Directory

  1. Create a project directory called hello_rest_server
  2. Copy the Gradle wrapper directory and the gradlew file to the project directory
  3. In the project directory create a file called settings.gradle containing rootProject.name = 'hello-rest-server'
  4. Create a Gradle build file called build.gradle in the project directory containing the following:

-------------------------------------------------------------------------------------
group 'org.example'version '0.1-SNAPSHOT'
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.5-2'    }
}

apply plugin: 'kotlin'apply plugin: 'application'
repositories {
    mavenCentral()
}

dependencies {
    compile 'org.jetbrains.kotlin:kotlin-stdlib:1.0.5-2'    compile 'org.glassfish.grizzly:grizzly-framework:2.3.28'    compile 'org.glassfish.jersey.containers:jersey-container-grizzly2-http:2.23.2'    compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.23.2'}

// A Task to run the program.run {
    mainClassName = 'org.example.hellorestserver.RestServerKt'    //args = ["arg1", "arg2"]}

// A Task to create a JAR file for the program.jar {
    from configurations.compile.collect { zipTree it }
    manifest.attributes 'Main-Class': 'org.example.hellorestserver.RestServerKt'}
-------------------------------------------------------------------------------------


Text version:

-------------------------------------------------------------------------------------
group 'org.example'
version '0.1-SNAPSHOT'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.5-2'
    }
}

apply plugin: 'kotlin'
apply plugin: 'application'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.jetbrains.kotlin:kotlin-stdlib:1.0.5-2'
    compile 'org.glassfish.grizzly:grizzly-framework:2.3.28'
    compile 'org.glassfish.jersey.containers:jersey-container-grizzly2-http:2.23.2'
    compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.23.2'
}

// A Task to run the program.
run {
    mainClassName = 'org.example.hellorestserver.RestServerKt'
    //args = ["arg1", "arg2"]
}

// A Task to create a JAR file for the program.
jar {
    from configurations.compile.collect { zipTree it }
    manifest.attributes 'Main-Class': 'org.example.hellorestserver.RestServerKt'
}
-------------------------------------------------------------------------------------

Create Kotlin Source Files


Create the following directory structure in the project directory, along with the kt files as shown below:

src
├── main
│   ├── java
│   ├── kotlin
│   │   └── org
│   │       └── example
│   │           └── hellorestserver
│   │               ├── HelloResource.kt
│   │               └── restServer.kt
│   └── resources
└── test
    ├── java
    ├── kotlin
    └── resources


To begin restServer.kt will act as the entry point file for the REST server, which will be edited first by adding the following code:

-------------------------------------------------------------------------------------
package org.example.hellorestserver

import org.glassfish.grizzly.http.server.HttpServer
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory
import org.glassfish.jersey.jackson.JacksonFeature
import org.glassfish.jersey.server.ResourceConfig
import javax.ws.rs.ProcessingException
import javax.ws.rs.core.UriBuilder


fun main(args: Array) {
    val HOST = "localhost"    val PORT = 9000    val baseUri = UriBuilder.fromUri("http://$HOST/").port(PORT).build()
    var server: HttpServer? = null
    try {
        server = GrizzlyHttpServerFactory.createHttpServer(baseUri, createConfiguration())
        println("REST Server Address: $HOST:$PORT")
    } catch (ex: ProcessingException) {
        println("Server Error: ${ex.message}")
        println("Exiting REST server...")
        server?.shutdown()
    }
}

private fun createConfiguration(): ResourceConfig {
    val config = ResourceConfig(HelloResource::class.java)

    // Optional but good practise to manually specify the JSON mapping implementation to use.    config.packages("org.example.hellorestserver").register(JacksonFeature::class.java)
    return config
}
-------------------------------------------------------------------------------------


Text version:

-------------------------------------------------------------------------------------
package org.example.hellorestserver

import org.glassfish.grizzly.http.server.HttpServer
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory
import org.glassfish.jersey.jackson.JacksonFeature
import org.glassfish.jersey.server.ResourceConfig
import javax.ws.rs.ProcessingException
import javax.ws.rs.core.UriBuilder


fun main(args: Array) {
    val HOST = "localhost"
    val PORT = 9000
    val baseUri = UriBuilder.fromUri("http://$HOST/").port(PORT).build()
    var server: HttpServer? = null

    try {
        server = GrizzlyHttpServerFactory.createHttpServer(baseUri, createConfiguration())
        println("REST Server Address: $HOST:$PORT")
    } catch (ex: ProcessingException) {
        println("Server Error: ${ex.message}")
        println("Exiting REST server...")
        server?.shutdown()
    }
}

private fun createConfiguration(): ResourceConfig {
    val config = ResourceConfig(HelloResource::class.java)

    // Optional but good practise to manually specify the JSON mapping implementation to use.
    config.packages("org.example.hellorestserver").register(JacksonFeature::class.java)
    return config
}
-------------------------------------------------------------------------------------

Above the host name and port number are stored in constants and referred to in the UriBuilder.fromUri function which creates a URI object. An attempt is made to start the server by creating an HttpServer object via the GrizzlyHttpServerFactory.createHttpServer function. Only 2 arguments need to be passed through, a URI and the resource configuration (ResourceConfig object).

Grizzly needs the resource configuration in order to know how to do the REST resource mapping. In this case the HelloResource class object (Java version) is passed as an argument to the ResourceConfig constructor in the defined createConfiguration function. If the server fails to start then a ProcessingException will be thrown. When that situation occurs the server error message is outputted to the console, followed by shutting down the server before exiting the program.

Now setup the REST resource mapping by adding the following code to HelloResource.kt:

-------------------------------------------------------------------------------------
package org.example.hellorestserver

import javax.ws.rs.GETimport javax.ws.rs.Pathimport javax.ws.rs.Producesimport javax.ws.rs.QueryParamimport javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response

@Path("/hello")
class HelloResource {
    @GET    @Produces(MediaType.APPLICATION_JSON)
    fun getMessage(@QueryParam("name") name: String): Response {
        println("Processing request...")
        return Response.ok(mapOf("msg" to "Hello $name! :)"), MediaType.APPLICATION_JSON).build()
    }
}
-------------------------------------------------------------------------------------


Text version:

-------------------------------------------------------------------------------------
package org.example.hellorestserver

import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.Produces
import javax.ws.rs.QueryParam
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response

@Path("/hello")
class HelloResource {
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    fun getMessage(@QueryParam("name") name: String): Response {
        println("Processing request...")
        return Response.ok(mapOf("msg" to "Hello $name! :)"), MediaType.APPLICATION_JSON).build()
    }
}
-------------------------------------------------------------------------------------

Above the HelloResource class handles the REST resource mapping for the /hello path (part of the URI). There is a single function (getMessage) which handles a GET HTTP request that contains a single URI query parameter called name. All that the function does is output a message to the console, and returns a message (as a HTTP 200 response via Response.ok function) in JSON (specified in the Produces annotation) form.

To start the REST server execute ./gradlew run in the project directory. Use the curl command to test out the server (eg curl localhost:9000/hello?name=Elvis). Exit the server using the Ctrl+c keyboard shortcut in the same console running the server.

No comments:

Post a Comment