Coder Social home page Coder Social logo

ktor-native's Introduction

Introduction

On my last post: Building a native cli with kotlin and graalvm, I've demonstrated how to build native cli application using graalvm. On this post I'll show how you can build a web application using ktor.io and the GraalVM.

Requirements

  • GraalVM installed and $GRAALVM_BIN pointing to the binary folder of your install

Building it

Ktor supports several server options (Netty, Jetty, CIO). Netty is my favorite option for java applications, but it can be quite painful trying to get netty to work with GraalVM as explained here

So I tried the CIO server (leverages only kotlin code, and coroutines). I then found out that GraalVM does not support the bytecode generated by coroutines.

Fortunately after some googling I bumped into kraal which seems to solve the problem.

The example will use the Gradle Kotlin DSL, just create a new Gradle project on intellij and mark "use Kotlin DSL"

From there modify your build.gradle.kts to look like this:

import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    kotlin("jvm") version "1.3.21"
    id("com.hpe.kraal") version "0.0.15" // kraal version - for makeRelease.sh
}

group = "io.igx.kotlin"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
    jcenter()
}

dependencies {
    implementation(kotlin("stdlib-jdk8"))
    implementation("org.slf4j:slf4j-simple:1.7.26")
    implementation("io.ktor:ktor-server-cio:1.1.3")
    implementation("io.ktor:ktor-gson:1.1.3")
}

tasks.withType<KotlinCompile>().configureEach {
    kotlinOptions {
        jvmTarget = "1.8"
        // need use-experimental for Ktor CIO
        freeCompilerArgs += listOf("-Xuse-experimental=kotlin.Experimental", "-progressive")
        // disable -Werror with: ./gradlew -PwarningsAsErrors=false
        allWarningsAsErrors = project.findProperty("warningsAsErrors") != "false"
    }
}


val fatjar by tasks.creating(Jar::class) {

    from(kraal.outputZipTrees) {
        exclude("META-INF/*.SF")
        exclude("META-INF/*.DSA")
        exclude("META-INF/*.RSA")
    }

    manifest {
        attributes("Main-Class" to "io.igx.kotlin.ApplicationKt")
    }

    destinationDirectory.set(project.buildDir.resolve("fatjar"))
    archiveFileName.set("ktor-native.jar")
}

tasks.named("assemble").configure {
    dependsOn(fatjar)
}

As usual, just edit the group/Main-Class to match your own packaging structure.

Let's make sure our app outputs some JSON based on a domain class:

data class Driver(val id: Int, val firstName: String, val lastName: String, val nationality: String)

Your Application should look like this one:

fun main(args: Array<String>) {
    val server = embeddedServer(CIO, 8080, module = Application::module)
    server.start(wait = true)
}

fun Application.module() {
    install(CallLogging)
    install(ContentNegotiation) {
        gson {
            setPrettyPrinting()
        }
    }
    routing {
       get("/drivers"){
           call.respond(Driver(102, "Ayrton", "Senna", "Brazilian"))
       }
    }
}

#Testing

run ./gradlew clean build and then java -jar build/fatjar/ktor-native.jar and your app should be app and running:

[DefaultDispatcher-worker-1] INFO ktor.application - No ktor.deployment.watch patterns specified, automatic reload is not active
[DefaultDispatcher-worker-1] INFO ktor.application - Responding at http://0.0.0.0:8080

Hit http://0.0.0.0:8080/drivers

{
"id": 102,
"firstName": "Ayrton",
"lastName": "Senna",
"nationality": "Brazilian"
}

Native image

As with our previous post, run the native-image command:

$GRAALVM_BIN/native-image  --report-unsupported-elements-at-runtime --jar build/fatjar/ktor-native.jar ktor-native --enable-url-protocols=http --no-server

run ./ktor-native, hit http://0.0.0.0:8080/drivers and you will get:

{}

Say Whaaat?

Reflection issues

So, GraalVM needs a little help handling reflection as explained here.

Add the following file to the root of your project (reflection.json)

[
   {
    "name" : "io.igx.kotlin.model.Driver",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true,
     "fields" : [
       { "name" : "id" },
       { "name" : "firstName" },
       { "name" : "lastName" },
       { "name" : "nationality" }
     ]
   },

  {
  "name" : "java.lang.Integer",
  "methods" : [{ "name" : "parseInt", "parameterTypes" : ["java.lang.String"]}]
}, {
  "name" : "java.lang.Long",
  "methods" : [{ "name" : "parseLong", "parameterTypes" : ["java.lang.String"]}]
}, {
  "name" : "java.lang.Boolean",
  "methods" : [{ "name" : "parseBoolean", "parameterTypes" : ["java.lang.String"]}]
}, {
  "name" : "java.lang.Byte",
  "methods" : [{ "name" : "parseByte", "parameterTypes" : ["java.lang.String"]}]
}, {
  "name" : "java.lang.Short",
  "methods" : [{ "name" : "parseShort", "parameterTypes" : ["java.lang.String"]}]
}, {
  "name" : "java.lang.Float",
  "methods" : [{ "name" : "parseFloat", "parameterTypes" : ["java.lang.String"]}]
}, {
  "name" : "java.lang.Double",
  "methods" : [{ "name" : "parseDouble", "parameterTypes" : ["java.lang.String"]}]
}
]

Run the native-image command again, but this time add the -H:ReflectionConfigurationFilesflag

~/java/graalvm-ce-1.0.0-rc13/Contents/Home/bin/native-image  --report-unsupported-elements-at-runtime -H:ReflectionConfigurationFiles=reflection.json  -jar build/fatjar/example.jar ktor-native --enable-url-protocols=http --no-server

Try it again and you will see the original results of your Driver JSON payload.

#Final Thoughts

Dealing with reflection on GraalVM can be a bit challenging, luckily to us, several people on the industry like the folks at RedHat are trying to come up with frameworks such as quarkus.io to overcome this limitation.

I have no doubt that IDEs will soon catch up and be able to inspect the code and generate those reflection files for us.

Meanwhile enjoy your blazing fast ktor server running as a native image on your server.

Happy Coding!

ktor-native's People

Contributors

viniciusccarvalho avatar

Stargazers

Indra Mahkota avatar Matthias Kellnhofer avatar Bogdan Cordier avatar 黃健旻 Vincent Huang avatar Lissa Rubí avatar Pavel Erokhin avatar Stefan Lobbenmeier avatar Marcos Sampaio avatar Yoon Kyong Sik avatar Yurii Huzii avatar kbendyk avatar  avatar Yanzai avatar Srinivasan avatar David Merrick avatar Dmitry avatar Roberto Estivill avatar  avatar Yuji Koyano avatar Dennis Scheffer avatar Andreas Mausch avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.