Kotlin: create REST services using Jersey and Jackson


Kotlin is a JVM language developed by the folks at Jetbrains.

It is a concise and compact language with 100% Java compatibility and 100% null-safety.

Another neat feature is its versatility: you can use the Kotlin language to create Java SE, Java EE, Android and JavaScript (Node.js) applications.

This blog will focus on using Kotlin to implement a simple Java SE application exposing a single REST endpoint using Jersey + Netty and Jackson.

  • First is an explanation of the syntax and highlights of Kotlin.
  • Second comes a short tutorial to illustrate how you can use Kotlin with Gradle to define your build script.
  • Third we look at a small tutorial of Jersey and how to fire it up by using a Netty HTTP server.
  • Fourth is a short explanation of how Jackson can be used in combination with Kotlin data classes.
  • Lastly, a small summary describes the created REST service and contains links to the used resources.

Exploring Kotlin

Syntax

The syntax of Kotlin is somewhere between Java and Scala. For example, the semicolon after a statement can be omitted.

Another thing to note is that you must specify whether the type is nillable. This is done by adding a question mark to the type, for example String versus String?.

The biggest difference, however, in comparison to Java is the way variables and methods are declared.

Declaring variables

When declaring a variable, you start with either val or var to denote if the variable is immutable or mutable.

After that follows the name and finally a colon with the type. An example of a simple String declaration:

The type can be omitted if the value is directly assigned:

Note that the above variables can never contain the value null, because we didn’t specify that username is nillable.

The following example would cause a compiler error:

To fix this we must explicitly specify that the type is nillable:

Using a nillable type without first doing a null-check also causes a compiler error:

To get the String’s length we must first validate that username is not null. This can be done with an if-statement, but luckily Kotlin also features a short-hand for doing null-checks:

Note that because username is nillable the resulting type of usernameLength is also nillable. So the deduced type of usernameLenght is Int?.

Declaring methods

When declaring a method you start with the keyword fun followed by the method’s name. After that follows the arguments and finally the return type. An example of a simple getter:

Parameters in a method follow the same form of declaration as those of variables. An example of a simple setter is as follows:

Note that the return type is omitted, which causes the method return type to automatically be Unit. This is the Kotlin equivalent of void.

Data classes

Kotlin introduces a new type of class called a data class. The purpose of this type is to design a class that only holds data.

Usually the fields in these kind of classes are immutable to guarantee thread-safety. The Kotlin compiler will automatically generate the equals(), hashCode()toString(), and copy() methods, which saves a lot of boiler-plate code.

To illustrate this, let’s implement a simple User class with a username, password and age. Although the definition sounds simple, the resulting class written in Java is still 50 lines of code:

Writing the same class in Kotlin only requires 1 line of code:

Note that in Kotlin we do not have to implement any method at all, because the methods implemented in the Java example are automatically generated for us.

Collections

Kotlin adds a whole bunch of useful (extension) methods to collections, such as map, flatMap, filter and forEach.

Contrary to Scala these collections are fully compatible with the java.util collections. The methods that are added by Kotlin are similar to the methods found in a Java 8 Stream.

The main difference is that collections are always finite whereas a Stream can potentially be infinite.

Let’s assume that someone wants to have a list of all unique passwords. We could implement this in Java 7 using a simple for loop:

Or we could implement this in Java 8 using a Stream:

What I personally don’t like in this approach is that we must first convert the List to a Stream, then execute the map method, and finally collect the result in a Set.

Implementing the same in Kotlin is far more elegant and readable (also note the syntax for adding elements to a List):

Companion objects

In Kotlin there is no static keyword. Instead you can define a companion object inside a class. To define a static constant inside a class, you do so inside the block companion object { ... }:

The constant MY_CONSTANT_INT is now available to use inside ExampleClass.

If you omitted the private modifier it would also be available in other classes as ExampleClass.MY_CONSTANT_INT, just like it works in Java.

Companion objects can also contain functions, which behave like static functions in Java:

Singletons

There are situations in which it is desirable to have only 1 instance of a class at any given time. This is known as a singleton.

Implementing a singleton in Java requires a few tricks to prevent multiple instances from being created:

In this example, we first define a final class (to prevent other from extending this class). Then we create a single static instance using a private constructor to ensure there is only 1 instance.

Finally, we implement a getInstance() method to expose the created instance.

Luckily in Kotlin we have the special type object for these situations.

The singleton shown above can be reduced to the following with Kotlin:

Gradle & Kotlin

Before diving into implementing a simple REST service, first choose a build tool to manage the dependencies and build the application. I personally prefer Gradle because it offers a lot of flexibility and is compatible with Maven.

Since version 3.x it is also possible to write the build script using Kotlin, which is awesome!

In order to use Kotlin as the language for the build script, we specify an alternative build file in the settings.gradle file (inspired by this example):

Another caveat is that you must opt-in to type-safe accessors, otherwise you will get compiler errors when using extension blocks such as application { ... }.

Read these release notes if you want to know why.

To do this, add the following line to the gradle.properties file:

Now we can write a nice statically-typed build script to configure our project:

Jersey + Netty

Jersey is a RESTful web services framework that enables exposing REST resources using a few simple annotations.

It is also the reference JAX-RS implementation that is used in GlassFish.

Although Jersey is a web services framework it does not implement a web server/container. Instead it requires the user to provide a ContainerProvider that starts a web container which Jersey can hook into.

For this blog we will be using Netty as our HTTP server implementation, because it is widely used and Jersey already has an implementation of ContainerProvider for Netty.

Creating a REST resource

To add Jersey and Netty to the classpath, add the following lines to the dependencies block in the Gradle build script:

Let’s take the class User that we defined earlier:

To expose a collection of User instances using a REST endpoint, first define a resource class:

This class defines a HashMap of User instances with their username as the key.

During constructor initialization the init block is executed, adding one entry to the map.

If you paid attention you would’ve noticed that the method getUser() returns User?, which means it can be null.

This is on purpose because the map might not contain a user with the given username. Also note the various JAX-RS (method) annotations. These are explained below.

Used JAX-RS annotations:

  • @Path
    Defines the HTTP path that can be used to reach a resource.
    Values between curly braces must be bound to a method parameter using @PathParam.
    When used on a class the specified path is prefixed to all methods inside the class.
    When used on a method the specified path is relative to the path of the class annotation.
  • @Produces
    Specifies the media type that should be used in the HTTP response.
    Can be used on a class, in which case the media type is used for all methods.
  • @GET
    Binds an HTTP GET request on the specified path to the annotated method.
  • @POST
    Binds an HTTP POST request on the specified path to the annotated method.
    The body of the request is automatically bound to the first non-annotated parameter.
  • @PUT
    Binds an HTTP PUT request on the specified path to the annotated method.
    The body of the request is automatically bound to the first non-annotated parameter.
  • @DELETE
    Binds an HTTP DELETE request on the specified path to the annotated method.
  • @PathParam
    Binds a path parameter (value between curly braces) to a method parameter.

Exposing the REST resource

The UserResource class is exposed by defining a class that extends Jersey’s Application class:

This class overrides the parent method Set<Object> getSingletons(). It returns a set with a single instance of UserResource in it.

Now we can define a simple singleton object that starts the Netty server:

In this class a ResourceConfig is created based on our JerseyApplication class. Then a Netty server is created and started. It is exposed on http://localhost:8080/.

However, if we try to GET http://localhost:8080/users/Gerard we get an HTTP 500 response. This is because we specified that the content should be serialized to JSON, but we didn’t implement a serializer.

Instead of implementing the serialization ourselves we will use Jackson.

Jackson & data classes

Jackson is a commonly used framework for serializing Java objects into JSON and vice versa. It is one of the few serialization frameworks that can invoke an user-defined constructor. This is very useful because Kotlin data classes don’t have a no-arg constructor (unless every field has a default value).

JAXB for example requires that classes have a no-arg constructor, making it unsuitable for our needs.

Configuring Jackson

To use Jackson with Jersey we must register Jersey’s JacksonFeature on the ResourceConfig instance.

Fortunately Jersey will automatically register this feature if it’s on the classpath. To do this, add the following line to the Gradle build script:

If we try to GET http://localhost:8080/users/Gerard again we will get a JSON response!

Hooray!

Now lets add a new user by sending a POST request to http://localhost:8080/users with the following JSON:

This will return an HTTP 400 response with the following body:

But what happened? Although Jersey can now successfully serialize our User class using Jackson, it is unable to deserialize such a class.

This is caused by the fact that Jackson does not know how to invoke the constructor of a Kotlin data class.

Deserializing data classes

In order to deserialize Kotlin data classes we have to register Jackson’s KotlinModule on an ObjectMapper instance.

This module can be added to the classpath by adding the following line to the Gradle build script:

Jackson does not automatically register new modules, so we have to do this ourselves. Open the object NettyServer that we created earlier and replace the following line:

with this line:

If we try the POST request again we will now get an HTTP 200 response. Sending a GET request to  http://localhost:8080/users/FooBar will now return the JSON we just POSTed.

It is possible to update the user information by sending a PUT request to http://localhost:8080/users/FooBar with the following JSON:

You can also delete an user by sending a DELETE request to http://localhost:8080/users/FooBar.

Summary

We have successfully created a simple REST service that can be used to create, read, update and delete user information. 

You can find the full code of the demo application at https://gitlab.com/craftsmen/kotlin-rest-service.

Also check out the following resources:

Een reactie plaatsen

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *