Paddling at the Edge of Ktor

Mike Broadway
5 min readJun 26, 2017

--

Until the Google I/O 2017 keynote announcement that Kotlin would be officially supported for Android development, I had ignored the language and knew nothing about it beyond that it was the brainchild of JetBrains. I am not an Android developer but Google’s endorsement woke me up to the presence of a serious alternative to Java, Scala and Clojure for server side JVM development; something akin to Scala but without the heavy syntactic sugar and steep learning curve.

Professionally, I am likely to try Kotlin incrementally on Dropwizard projects that take advantage of its interoperability with Java. In my personal time, I can afford to be more experimental. I have a moribund photography web site that has been languishing without an update in five years — how about killing two birds with one stone: learn about Kotlin by using the Kotr web application framework to create a new vanity site for the few photographs that I still want to show.

Building a new art portfolio web site with Kotr may be a pipe dream, but its a good way to learn how some experienced Kotlin programmers use the language and it’s new coroutines to implement a significant project. However, before I can get to the cool async stuff I have to be able to construct and run a basic Ktor application.

The Ktor documentation is, understandably, very much in its infancy at this point and a Google search is as likely to return a Star Wars: Knights of the Old Republic (KotOR) game link as a Stack Overflow answer on Kotr! Figuring out how to build and run the simplest, 10 line, “Hello World” application took a couple of hours. Here’s what I found out, maybe it will save the next person a few minutes:

A Skeleton Application

With a bit of poking around in the Kotr Wiki you will eventually find the following code snippet under the Features topic (Features being elements that you can install into the request and response pipeline):

fun Application.main() {
install(DefaultHeaders)
install(CallLogging)
install(Routing) {
get("/") {
call.respondText("Hello, World!")
}
}
}

Well, that looks simple enough but how would you know what to import so that it could be compiled? If you do more digging, including cloning the Kotr source code to your laptop, you might find the full source code in the HelloApplication.kt file of the ktor-samples-hello demo project. I am not a fan of wildcarding for imports so here’s my version:

import org.jetbrains.ktor.application.Application
import org.jetbrains.ktor.application.call
import org.jetbrains.ktor.application.install
import org.jetbrains.ktor.features.DefaultHeaders
import org.jetbrains.ktor.logging.CallLogging
import org.jetbrains.ktor.response.respondText
import org.jetbrains.ktor.routing.Routing
import org.jetbrains.ktor.routing.get

To compile this with Maven, you will need to reference the Kotr repository:

<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>bintray-kotlin-ktor</id>
<name>bintray</name>
<url>https://dl.bintray.com/kotlin/ktor</url>
</repository>
</repositories>

And add these properties and dependencies:

<properties>
...
<jvm.target>1.8</jvm.target>
<kotlin.version>1.1.2-5</kotlin.version>
<ktor.version>0.3.3</ktor.version>
...
</properties>

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

And finally, the Kotlin compiler plugin:

<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<configuration>
<jvmTarget>${jvm.target}</jvmTarget>
</configuration>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>

But Where Is The main(…) Function?

Wonderful, with a bit of luck and suffering, you can now compile the Application.main() function but how do you start the app? Where is the main(...) function in a free standing Kotr web application?

As of this writing, the Ktor sample code README.md instructions for running a Kotr application under IntelliJ IDEA are out of date. To run with Netty, the class you want to point to is org.jetbrains.ktor.netty.DevelopmentHost. It is this that provides the main(args: Array<String>) that you need.

To run from the Maven command line with mvn exec:java you need to configure the POM with the exec-maven-plugin plugin as follows:

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<mainClass>org.jetbrains.ktor.netty.DevelopmentHost</mainClass>
</configuration>
</plugin>

But you are not done yet. You still need to provide the Kotr framework with its configuration (what port it is to listen to etc) and how it should do its logging.

Configuring the “Application Host”

Using IntelliJ Idea to drill into the Kotr code, the DevelopmentHost class containing the main(...) function is implemented in a file named CommandLine.kt. The first thing it does is configure an ApplicationHostEnvironment object from either command line parameters or a default application.conf file. The function finishes by passing the environment to a NettyApplicationHost object and starting that. For this to work, you have to provide the application.conf file in your resources directory; here's one ripped off from the Ktor 'Hello World' example:

ktor {
deployment {
port = 8080
autoreload = true
watch = [ hello ]
}

application {
modules = [ com.mikebway.ktor.WebSiteKt.main ]
}
}

For my test project, theWebSiteKt portion in the com.mikebway.ktor.WebSiteKt.main module name is derived from the WebSite.kt source filename that I used for theApplication.main() function code at the top of this post. The Calling Kotlin from Java section of the language documentation provides a little more discussion of how this JVM class name is derived (Application.mainis the only main function defined in the file so Application need not be specified).

Logging Configuration

To enable logging we must add SLF4J and Logback to the dependencies in the POM:

<properties>
...
<logback.version>1.2.1</logback.version>
<slf4j.version>1.7.12</slf4j.version>
...
</properties>

<dependencies>
...
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
...
<dependencies>

And provide the Logback configuration in a resources/logback.xml file:

<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<root level="trace">
<appender-ref ref="STDOUT"/>
</root>

<logger name="io.netty" level="INFO"/>

</configuration>

Shading For Command Line Execution

The final piece of the puzzle, to have a skeleton web application that can be run independently of Maven or an IDE is to produce a shaded jar file with a manifest that allows the application to be started with a simple command along the lines of:

java -jar ktor-portfolio-0.5-SNAPSHOT.jar

That just requires adding the maven-shade-plugin to the POM and configureingit with the org.jetbrains.ktor.netty.DevelopmentHost main class:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<executions>
<!-- Run shade goal on package phase -->
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<!-- add Main-Class to manifest file -->
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.jetbrains.ktor.netty.DevelopmentHost</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>

Conclusion

There is little to no original contribution here, pretty much everything that I have offered comes from the Kotr github project and Wiki. All I have done is pulled it together in one place.

The source code for my project, tagged with ‘skeleton’ to match this post, can be found here in github.

--

--

Mike Broadway

A software developer, a some-time photographer, a public radio supporter, an occasional reader of long books and a Distinguished Engineer at ZenBusiness.com.