Tuesday, 21 November 2017

Kotlin RESTful Microservices using Open Liberty


Kotlin is not a new language, but there's no doubt it's received a huge boost with the announcement earlier this year that it was now an official language for Android development.  As Kotlin use grows on Android, so does the need for Kotlin on the server.  "Backend for frontend" is the concept of having server-side components that are aligned with the front-end user experience.  Both are usually created and maintained by the same team and so using the same technologies makes a lot of sense.  For example, Node.js services supporting a JavaScript-based Web frontend, or Java services supporting an Android front-end.  So this raises the question, "What backend can I use for a Kotlin front-end?".

New languages often struggle to gain adoption early on because they're missing the richness of frameworks and standards that communities provide over time.  Kotlin has cleverly avoided this problem by being fully interoperable with Java.  This means that, for example, to create a RESTful backend in Kotlin, we can make use of existing Java server-side technologies, such as those supported by Eclipse MicroProfile (e.g. JAX-RS, JSON-P, etc).

To that end, I thought I'd write a little sample just to see what the experience was like...

Disclaimer:  I'm not a Kotlin expert.  I can spell it (most of the time) and occasionally get the code to compile.  If you are a Kotlin expert, go easy, or even send me a PR if you spot bad stuff, so I can learn.  Thanks :)

The Sample

The code for what I created is at - https://github.com/gcharters/Kotlin-REST-Service.  There's a README that explains how to build and run it.

The sample is essentially a simple REST service implemented using JAX-RS.  The service is very, very basic and doesn't really take advantage of any Kotlin features other than its interoperability with Java and its concise syntax.  For now, the goal is to just show how to get things working together.

I created the sample by first using the liberty-archetype-webapp to generate a sample web app project with build and a test.  I then gutted it and replaced it with a REST service written in Java, then finally, I rewrote everything in Kotlin.  Ideally there'd be a liberty-archetype-rest and I'd have written the service straight off in Kotlin, but this was a learning exercise so I wanted to take baby steps.

There are four main parts to the sample:
  1. The maven build - Kotlin-REST-Service/pom.xml
  2. The server configuration - Kotlin-REST-Service/src/main/liberty/config/server.xml
  3. The service - Kotlin-REST-Service/src/main/kotlin/gcc/kt/rest/HelloService.kt
  4. The test case - Kotlin-REST-Service/src/test/kotlin/gcc/kt/rest/it/HelloServiceIT.kt

The build

The build is maven-based and uses the liberty-maven-plugin to work with the Open Liberty server - create a server package, start/stop the server, and so on.  It uses the maven-war-plugin to build a WAR file containing the Kotlin REST application, and the maven-failsafe-plugin for running integration tests.  The last, but rather important, plugin to mention that's used is the kotlin-maven-plugin to compile the Kotlin code.  The maven build in this sample only compiles Kotlin code; if you want to compile Kotlin and Java in the same project you'll need to use the Compiling Kotlin and Java sources configuration.

All but one of the dependencies in the maven pom are the ones you would use when writing a JAX-RS Java REST service.  The one that's new is for the Kotlin language library:

 <dependency>  
   <groupId>org.jetbrains.kotlin</groupId>  
   <artifactId>kotlin-stdlib</artifactId>  
   <version>${kotlin.version}</version>  
 </dependency>   


The absence of an explicit maven scope for this dependency means it will be packaged in the WAR file, as it's required at runtime.  There are other approaches we could take, such as adding it as a shared or global library or a Liberty feature, but this is the simplest.  The downside is every app you build this way will have its own copy of Kotlin.  It's not a large library, but the more copies you have, the more you have to patch if there are any issues (e.g. security vulnerabilities, etc).

The server

There is nothing unique about the server configuration.  It specifies that we only need the JAX-RS capability, what ports to use, and then the application to be deployed.  The one thing to note is that the context root for the application will be the value specified in the name attribute of the webApplication element - i.e., "helloKotlin".

  <webApplication id="kotlinHello" location="kotlinHello.war"
     name="kotlinHello"/>  

The service

If you're familiar with JAX-RS, then you'll recognize the JAX-RS annotations and inheritance for specifying the application and path information, in the service implementation.  When deployed for testing, the WAR context root is "helloKotlin" (see the earlier server configuration), the application path is "/", the path to the service is "/hello" and on an HTTP GET, the service is expecting a "name" path parameter.  There's no error handling, so it breaks in ugly ways if you don't get the URL right. 

An example request would be:

http://localhost:9080/helloKotlin/hello/gcharters

 @Path("/hello")  
 @ApplicationPath("/")  
 class HelloService : Application() {  
   @GET  
   @Path("/{name}")    
   fun sayHello(@PathParam("name") name: String): Response {  
     println("HelloService sayHello called: " + name)  
     val response = Response.ok("Hello " + name).build()  
     return response  
 }  

The test case

The test case is very basic, just checking that it gets an HTTP return code of 200 from the service.  The location of the service to test is built up from values provided by the maven-failsafe-plugin as system properties .  The request itself is made using the JAX-RS Client (introduced in JAX-RS 2.0).
     // Set up the path to the service  
     val port = System.getProperty("liberty.test.port")  
     val contextName = System.getProperty("app.context.root")  
     val path = "hello"  
     val person = "Fred"  
     val url = "http://localhost:" + port + "/" + contextName 
               + "/" + path + "/" + person 

     // Make the request  
     val client = ClientBuilder.newClient()  
     val target = client.target(url)  
     val response = target.request().get() 

     // Test we got an OK response  
     assertEquals("Incorrect response code from " + url, 200, 
                  response.getStatus())  

Tools

In the course of writing the sample, I tried a couple of tools options.  The Eclipse IDE with the Open Liberty Tools and the Kotlin tools gives you a reasonable experience.  I also tried out VS Code with Kotlin Language extension.  This is ok for syntax highlighting, but is not a language server (there is, as yet, no language server for Kotlin, and this post would suggest there may not be one for a while).  The lack of language server means you need to run a maven build each time you make changes to see the results.

Summary

Kotlin is definitely an interesting language and one with what looks like a bright future.  If you're looking for a server-side option for Kotlin, using it with Open Liberty and picking from the existing MicroProfile and Java EE technologies is a great place to start.

There's a lot more to Microservices than writing a REST service, but the great thing about re-using existing backend technologies with Kotlin is you inherit all of the existing capabilities.  If you're already deploying the chosen backend infrastructure, there's no need to make new choices around how to do containerization, clustering, configuration, security, etc., all of your approaches simply apply as-is.

No comments: