4
votes

I am trying to build up an application from Ktor which eventually should be executed via a fatjar. A fatjar allows the use of arguments like the following one:

java -jar myApp.jar XXXXX YYYYY

I know how to get the XXXXX in the main module (by using simple args[0]) but I have troubles to get that values in the Application modules.

I would like to do something like that:

fun main(args: Array<String>) {
    val port = System.getenv("PORT")?.toInt() ?: 8080
    val status = args[0]
    embeddedServer(Netty, port = port, module = (Application::mainModule())).start(wait = true)
}

fun Application.mainModule() {
    routing {
        get("/status") {
            call.respondText(
                <status variable from main function>,
                contentType = ContentType.Text.Html
            )
        }
    }
}
2

2 Answers

3
votes

You can replace the method reference for the module parameter with a normal lambda. Within this lambda you invoke the module function with parameters you want:

fun Application.mainModule(args: Array<String>) {
    routing {
        get("/status") {
            //...
        }
    }
}

fun main(args: Array<String>) {
    val server = embeddedServer(Netty, port = 8080) {
        mainModule(args)
    }
    server.start(wait = true)
}

Details

The function embeddedServer has module as last parameter. The type of the parameter is a function of the type Application without any parameter and with Unit as return value:

fun embeddedServer(..., module: Application.() -> Unit) : TEngine

That is the reason you can provide Application::mainModule a as function reference. It exactly matches the type definitions of the parameter. But you could also provide a trailing lambda instead of this function reference:

val server = embeddedServer(Netty, port = 8080) { 
    // this is Application
    routing {
        get("/status") {
            //...
        }
    }
}

Within this lambda this is of type Application exactly as in your function Application.mainModule. That means in this lambda you can easily invoke other functions of type Application. In my answer I created the Application.mainModule with args as parameter and invoke this function within the trailing lambda.

2
votes

To pass a new configuration parameter, you can prepend it with -P:propName=.

So that if you want to pass a property named csv with a value from a command line, you can do the following:

fun main(args: Array<String>) {
    val csvFileName = "-P:csv=${args[0]}"
    EngineMain.main(arrayOf(csvFileName))
}

You can also pass the parameters without the need of modifying args by using -P:csv=myfile.csv as a command-line parameter.

Then, in your modules, you can access it via environment.config.property("csv").getString().

fun Application.module() {
    val csvFileName = environment.config.property("csv").getString()
    println(csvFileName)
}

You can read more about configuring Ktor application in the official Ktor documentation.