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
- Create a project directory called hello_rest_server
- Copy the Gradle wrapper directory and the gradlew file to the project directory
- In the project directory create a file called settings.gradle containing rootProject.name = 'hello-rest-server'
- 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.