Hello there.
I am using ktor 0.3.2 with netty, kotlin1.1 and Java8.
And thank you to make those great framework.
I'm trying to make JDBC middle ware for communication .NET main server via RESTful way.
However i have a problem to call.respond in non-suspended function.
post("query") {
Connection.connect {
// call.respond occurs an error. (Message: Suspension functions can be called only within coroutine body)
call.respond(JsonResponse(query("SELECT * FROM someTable").toList())
}
}
Connection.connect function is non-suspended function.
I think the functions as post(), get() is using lambda function that chanined by pipeline.
And that pipeline is also represented PipelineContext, And that function is suspended function.
However Connection.connect function can't be implemented pipeline patterns that designed ktor.
How do i solve this problem?
This is the source part of Connection.connect.
class Connection private constructor() {
companion object {
fun connect(init: Database.() -> Unit): Database {
return connect("default", init);
}
fun connect(name: String, init: Database.() -> Unit): Database {
var properties: Properties = Properties()
val config: HashMap<String, Object>? = Config.get(name)
if (config === null) {
throw ConfigureNotFoundException("The configuration for connection to JDBC is not founded.")
}
DriverManager.registerDriver(com.amazonaws.athena.jdbc.AthenaDriver())
val conn: Connection = DriverManager.getConnection(
config.get("host")?.toString(),
config.get("name")?.toString(),
config.get("password")?.toString()
)
var database = Database(conn)
database.init()
return database
}
}
}
Thank you.
I solved this issue myself.
Thank you.
@KennethanCeyer would be good let people know what the solution was...

Hello all you guys :clap:
This is very old issue...
I almost forgotten what the problem was.. :cry:
However perhaps i had ignored contribution culture at Kotlin's ecosystem then.
So i would try to remember what the solution was now!
In this issue, The problem was the suspend function(like as call.response) should not be under the blocking body (like as Connection.connect).
This problem seems to be similar to the problem that the async function in other languages does not await under the sync function.
post("query") {
Connection.connect {
// call.respond occurs an error. (Message: Suspension functions can be called only within coroutine body)
// query
call.respond(JsonResponse(query("SELECT * FROM someTable").toList())
}
}
You could know that application.response or call.response is suspend inline function
Please check this link and, https://github.com/ktorio/ktor/blob/37cbb5c59e3c5ae26fd7d2185a26f6e23e076802/ktor-server/ktor-server-core/src/io/ktor/response/ApplicationResponseFunctions.kt#L11
However Connection.connect body is none-coroutine(blocking function) type :open_mouth:
class Connection private constructor() {
companion object {
...
// blocking function
fun connect(name: String, init: Database.() -> Unit): Database {
When I looked at Commit in the past, I seem to have solved it like follows.
open class Connection private constructor() {
companion object {
suspend fun connect(init: suspend Database.() -> Unit): Database {
...
}
suspend fun connect(name: String, init: suspend Database.() -> Unit): Database {
...
}
suspend fun connect(credential: Credential, init: suspend Database.() -> Unit): Database {
...
}
}
All function had been changed to suspend(coroutine) type
@location("/controller-prefix/query")
data class ControllerName(val credential: Credential, val query: String)
fun Route.controllerName() {
location<ModelName> {
method(HttpMethod.Post) {
handle<ModelName> {
Connection.connect(it.credential) {
call.respond(JsonResponse(query(it.query).toList()))
}
}
}
}
}
At the time, I was not familiar with Kotlin,
So i did not share because I didn't know what is the Best Practices
I know now that many people are suffering from this. 馃槆
Thanks @KennethanCeyer! This lead me down the correct path...
For reference - https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md#wrapping-callbacks is the correct way to wrap callbacks within a coroutine and avoid.
Suspension functions can be called only within coroutine body
So using your example above in an inline fashion...
post("query") {
Connection.connect {
// call.respond occurs an error. (Message: Suspension functions can be called only within coroutine body)
// query
call.respond(JsonResponse(query("SELECT * FROM someTable").toList())
}
}
Becomes
post("query") {
val response = suspendCoroutine<List> { cont ->
Connection.connect {
val result = query("SELECT * FROM someTable").toList()
cont.resume(result)
}
}
call.respond(JsonResponse(response))
}
Most helpful comment
@KennethanCeyer would be good let people know what the solution was...