kotlin dsl

Do you know that you can create a Kotlin DSL with a few lines of code? In this post, we will see how to create a DSL over theApache HttpComponents library

Kotlin is a JVM language. This is how is described in its page:

Statically typed programming language for modern multiplatform applications
100% interoperable with Java™ and Android™

It has a lot of features and you can use to build your Android apps. We will focus on Type Safe Builders the feature that let us create DSL

Defining a Kotlin DSL for a Rest Client

At the end of this post, we can write code like this with our Kotlin DSL

GET("http://www.google.com") {
  withParam("foo", "bar")
  withHeader("Accept", "text/plain")
}

Looks like magic, doesn’t it?

First of all we will create our pom.xml to add the dependencies and config the Kotlin jars

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>es.rubenjgarcia</groupId>
  <artifactId>rest-dsl</artifactId>
  <version>0.1.0-SNAPSHOT</version>

  <properties>
    <kotlin.version>1.1.51</kotlin.version>
  </properties>

  <dependencies>
    <dependency>
        <groupId>org.jetbrains.kotlin</groupId>
        <artifactId>kotlin-stdlib</artifactId>
        <version>${kotlin.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.5.3</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
    <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
    <plugins>
        <plugin>
            <artifactId>kotlin-maven-plugin</artifactId>
            <groupId>org.jetbrains.kotlin</groupId>
            <version>${kotlin.version}</version>

            <executions>
                <execution>
                    <id>compile</id>
                    <goals>
                      <goal>compile</goal>
                    </goals>
                </execution>

                <execution>
                    <id>test-compile</id>
                    <goals>
                      <goal>test-compile</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
  </build>
</project>

In the directory src/main/kotlin we will create the RestDSL.kt with the next content

import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpRequestBase
import org.apache.http.client.utils.URIBuilder
import org.apache.http.impl.client.HttpClients
import org.apache.http.util.EntityUtils

class RestBuilder(private val request: HttpRequestBase) {

    private var params: List<Pair<String, String>> = emptyList()
    private var headers: List<Pair<String, String>> = emptyList()

    fun withParam(name: String, value: String) {
        this.params = this.params + Pair(name, value)
    }

    fun withHeader(name: String, value: String) {
        this.headers = this.headers + Pair(name, value)
    }

    fun exec(): HttpResponse {
        val httpclient = HttpClients.createDefault()
        if (params.isNotEmpty()) {
            val uriBuilder = URIBuilder(request.uri)
            for ((first, second) in params) {
                uriBuilder.addParameter(first, second)
            }
            request.uri = uriBuilder.build()
        }

        for ((first, second) in headers) {
            request.addHeader(first, second)
        }

        httpclient.execute(request).use { r ->
            val entity = r.entity
            EntityUtils.consume(entity)
            return r
        }
    }
}

fun GET(url: String, init: RestBuilder.() -> Unit) = RestBuilder(HttpPost(url)).apply(init).exec()

Let me explain the code

  • The function GET receives a String and a lambda expression. The magic is in the lambda expression init. Everything inside has a reference to the RestBuilder object so we can invoke functions in a DSL way. We apply (invoke) the function and then we call the exec function
  • We have 2 functions withParam and withHeader to add params and headers to our request
  • The function exec is where we execute the call. We add the params and the headers using the API provided by the Apache HttpComponents library and return the response

As you can see it’s easy to create a Kotlin DSL to implement the GET function so now you can implement the other functions: POST, DELETE, PATCH, …