InsertKoinIO / koin

Koin - a pragmatic lightweight dependency injection framework for Kotlin & Kotlin Multiplatform
https://insert-koin.io
Apache License 2.0
9.12k stars 721 forks source link

Ktor no longer working in 3.5.0 #1656

Closed nielsvanvelzen closed 1 year ago

nielsvanvelzen commented 1 year ago

Describe the bug

Starting with version 3.5.0 Koin is not working inside a ktor server anymore and throws the following exception:

Exception in thread "main" java.lang.IllegalStateException: No instance for key AttributeKey: KOIN
    at io.ktor.util.Attributes$DefaultImpls.get(Attributes.kt:62)
    at io.ktor.util.AttributesJvmBase.get(AttributesJvm.kt:15)
    at org.koin.ktor.ext.ApplicationExtKt.getKoin(ApplicationExt.kt:34)
    at [...].api.ApiServiceKt$ktorServerModule$$inlined$inject$default$1.invoke(ApplicationExt.kt:78)

(stacktrace reduced to only the API part of our application)

I couldn't find anything in the changelog but the ktor documentation page now says "Koin Ktor plugin uses isolated Koin context. You won't be able to start Koin outside of Ktor". Which might explain this behavior. This won't do for us though as the ktor part of our app is just one of many parts, and Koin must me initialized on a higher level.

To Reproduce Steps to reproduce the behavior:

  1. Create a Kotlin application
  2. Initialize Koin
  3. Initialize Ktor and try to use Koin in the initialization code
  4. See error

Expected behavior Koin should just work, like in previous versions.

Koin module and version: 3.5.0

Snippet or Sample project to help reproduce

This snippet reproduces the issue.

fun main() {
    val app = koinApplication {
        module {
            single<String> { "Reproduction test" }
        }
    }

    embeddedServer(
        Netty,
        module = {
            val test by inject<String>()
            println(test)
        },
    ).start()
}
arnaudgiuliani commented 1 year ago

yep, I saw that. A patch can be done quickly

arnaudgiuliani commented 1 year ago

@nielsvanvelzen in your case, do you use koinApplication or startKoin to run Koin outside of Ktor?

nielsvanvelzen commented 1 year ago

I forgot to add it in the reproducible sample but we do actually call startKoin a few lines down.

    val koinApp = koinApplication {
        // Add modules
        modules(
            // ....
        )
    }

    // ....
    startKoin(koinApp)
dadadom commented 1 year ago

Another reproducer, aka issue: This example fails with 3.5.0 but works with 3.4.3:

import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.*
import io.ktor.server.response.respond
import io.ktor.server.routing.*
import io.ktor.server.testing.testApplication
import org.junit.jupiter.api.Test
import org.koin.dsl.module
import org.koin.ktor.plugin.Koin

class ReproducerTest {

    @Test
    fun `minimalistic reproducer`() {
        testMyApplication {
            val response = it.get("testurl") {}

            response.status.shouldBe(HttpStatusCode.OK)
            response.bodyAsText().shouldContain("Test response")
        }
    }

    private fun testMyApplication(test: suspend (jsonClient: HttpClient) -> Unit) = testApplication {
        application {
            install(Koin) {
                modules(
                    module {
                        single { this@application }
                        single(createdAtStart = true) { KtorMyModule(get()) }
                    },
                )
            }
        }
        test.invoke(createClient {})
    }
}

class KtorMyModule(application: Application) {
    init {
        application.routing {
            get("testurl") { call.respond(HttpStatusCode.OK, "Test response") }
        }
    }
}
arnaudgiuliani commented 1 year ago

Will deploy Koin 3.5.1 to help unlock for ktor

hnljp commented 1 year ago

@arnaudgiuliani funny, for us, koin 3.5.0 was working fine with ktor, but 3.5.1 is failing, reporting No Koin instance started. Use install(Koin) or startKoin()

I will try to investigate it further at a later time, but for now we will return to use koin 3.5.0, that is working fine with ktor (at least for us).

nielsvanvelzen commented 1 year ago

I've just tested the update and everything is working again for us with 3.5.1. Thanks for the fast response!

hnljp commented 1 year ago

@arnaudgiuliani funny, for us, koin 3.5.0 was working fine with ktor, but 3.5.1 is failing, reporting No Koin instance started. Use install(Koin) or startKoin()

I will try to investigate it further at a later time, but for now we will return to use koin 3.5.0, that is working fine with ktor (at least for us).

@arnaudgiuliani Moving "createdAtStart" from the module to the individual single's, fixed my issue with koin 3.5.1 (again, everything was working fine prior to 3.5.1).

arnaudgiuliani commented 1 year ago

@hnljp can you share more about your config and usage of Koin. Seems that your are using it out of Koin Ktor plugin?

samzurcher commented 1 year ago

We have used the following pattern in our Ktor application:

install(Koin) { configure() }
install(StatusPages) { configure(get(LoggerFactory::class.java)) }

This works perfectly in 3.4.1 but does not work in 3.5.x and fails with KoinApplication has not been started. Bug in Koin? Or what would be the suggested pattern?

arnaudgiuliani commented 1 year ago

There was a problem in 3.5.0. Are you using koin-ktor 3.5.1 @samzurcher?

samzurcher commented 1 year ago

Yes, I was using 3.5.1. We have now added a manual startKoin(this) to start koin and it is working now.

arnaudgiuliani commented 1 year ago

weird, don't understand what's changed here for you 🤔