Skip to content

Commit 53cfc64

Browse files
Open KoinApplication to allow add KoinKtorApplication DSL Bridge for Ktor DI. Explicit options for bridging Ktor -> Koin & Koin -> Ktor
1 parent 5c8c342 commit 53cfc64

File tree

8 files changed

+193
-49
lines changed

8 files changed

+193
-49
lines changed

projects/core/koin-core/src/commonMain/kotlin/org/koin/core/KoinApplication.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import kotlin.time.measureTime
3434
*/
3535
@OptIn(KoinInternalApi::class)
3636
@KoinApplicationDslMarker
37-
class KoinApplication private constructor() {
37+
open class KoinApplication protected constructor() {
3838

3939
val koin = Koin()
4040
private var allowOverride = true
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2017-Present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.koin.core
17+
18+
import io.ktor.server.application.Application
19+
import io.ktor.server.application.install
20+
import io.ktor.server.plugins.di.DI
21+
import org.koin.core.annotation.KoinExperimentalAPI
22+
import org.koin.core.annotation.KoinInternalApi
23+
import org.koin.core.module.KoinApplicationDslMarker
24+
import org.koin.ktor.di.KoinDependencyMap
25+
import org.koin.ktor.di.KtorDIExtension
26+
27+
/**
28+
*
29+
*/
30+
@OptIn(KoinInternalApi::class)
31+
@KoinApplicationDslMarker
32+
class KoinKtorApplication() : KoinApplication() {
33+
34+
var ktorApplication : Application? = null
35+
var ktorBridge : KtorBridgeDSL? = null
36+
37+
/**
38+
* Setup Bridge options for Koin & Ktor
39+
*
40+
* @ee KtorDSL
41+
*/
42+
@KoinExperimentalAPI
43+
fun bridge(option : KtorBridgeDSL.() -> Unit){
44+
ktorBridge = KtorBridgeDSL()
45+
ktorBridge!!.option()
46+
}
47+
48+
internal fun onPostStart(){
49+
ktorBridge?.let { ktorBridge ->
50+
if (ktorBridge.bridgeKoinToKtor){
51+
onBridgeKoinToKtor()
52+
}
53+
if (ktorBridge.bridgeKtorToKoin){
54+
onBridgeKtorToKoin()
55+
}
56+
}
57+
}
58+
59+
internal fun onBridgeKoinToKtor(){
60+
koin.logger.debug("Ktor DI Bridge: Koin -> Ktor")
61+
62+
koin.resolver.addResolutionExtension(KtorDIExtension(ktorApplication ?: error("KoinKtorApplication has no ktorApplication, when using koinToKtor()")))
63+
}
64+
65+
internal fun onBridgeKtorToKoin(){
66+
koin.logger.debug("Ktor DI Bridge: Ktor -> Koin")
67+
68+
val ktorApp = ktorApplication ?: error("KoinKtorApplication has no ktorApplication, when using ktorToKoin() ")
69+
ktorApp.install(DI){
70+
include(KoinDependencyMap(koin))
71+
}
72+
}
73+
74+
companion object {
75+
76+
fun init() : KoinKtorApplication {
77+
return KoinKtorApplication()
78+
}
79+
}
80+
}
81+
82+
/**
83+
*
84+
*/
85+
@OptIn(KoinInternalApi::class)
86+
@KoinApplicationDslMarker
87+
class KtorBridgeDSL {
88+
89+
internal var bridgeKoinToKtor : Boolean = false
90+
private set
91+
92+
internal var bridgeKtorToKoin : Boolean = false
93+
private set
94+
95+
@KoinExperimentalAPI
96+
fun koinToKtor() {
97+
bridgeKoinToKtor = true
98+
}
99+
100+
@KoinExperimentalAPI
101+
fun ktorToKoin() {
102+
bridgeKtorToKoin = true
103+
}
104+
}

projects/ktor/koin-ktor/src/commonMain/kotlin/org/koin/ktor/di/KoinDependencyMap.kt

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,45 +18,60 @@ package org.koin.ktor.di
1818
import io.ktor.server.application.Application
1919
import io.ktor.server.plugins.di.*
2020
import org.koin.core.Koin
21+
import org.koin.core.annotation.KoinInternalApi
2122
import org.koin.core.error.NoDefinitionFoundException
2223
import org.koin.core.qualifier.Qualifier
2324
import org.koin.core.qualifier.named
2425
import org.koin.ktor.plugin.koin
2526

26-
/**
27-
* Full DependencyMapExtension integration for Koin with Ktor 3.2 DI
28-
*
29-
* This implementation provides seamless integration by implementing the
30-
* DependencyMapExtension interface, allowing Ktor DI to automatically
31-
* resolve dependencies from Koin when not found in Ktor's registry.
32-
*/
33-
class KoinDependencyMapExtension : DependencyMapExtension {
34-
35-
override fun get(application: Application): DependencyMap {
36-
return KoinDependencyMap(application.koin())
37-
}
38-
}
27+
///**
28+
// * Full DependencyMapExtension integration for Koin with Ktor 3.2 DI
29+
// *
30+
// * This implementation provides seamless integration by implementing the
31+
// * DependencyMapExtension interface, allowing Ktor DI to automatically
32+
// * resolve dependencies from Koin when not found in Ktor's registry.
33+
// */
34+
//class KoinDependencyMapExtension : DependencyMapExtension {
35+
//
36+
// override fun get(application: Application): DependencyMap {
37+
// return KoinDependencyMap(application.koin())
38+
// }
39+
//}
3940

4041
/**
4142
* Koin implementation of DependencyMap interface
4243
*/
44+
@OptIn(KoinInternalApi::class)
4345
class KoinDependencyMap(private val koin: Koin) : DependencyMap {
4446

45-
override fun contains(key: DependencyKey): Boolean =
46-
try {
47+
init {
48+
println("[DEBUG] KoinDependencyMap init")
49+
}
50+
51+
val koinLogger = koin.logger
52+
53+
override fun contains(key: DependencyKey): Boolean{
54+
koin.logger.debug("contains $key ?")
55+
return try {
4756
resolve(key)
4857
true
4958
} catch (e: NoDefinitionFoundException) {
5059
false
5160
}
61+
}
5262

53-
override fun getInitializer(key: DependencyKey): DependencyInitializer =
54-
DependencyInitializer.Explicit(key) {
63+
override fun getInitializer(key: DependencyKey): DependencyInitializer{
64+
koin.logger.debug("getInitializer $key ?")
65+
66+
return DependencyInitializer.Explicit(key) {
5567
resolve(key)
5668
}
69+
}
5770

5871
// Here we are using Any because we do not have the type
5972
private fun resolve(key: DependencyKey): Any {
73+
koin.logger.debug("resolve $key ?")
74+
6075
val clazz = key.type.type
6176
val qualifier = key.toQualifier()
6277
return koin.get(clazz, qualifier)

projects/ktor/koin-ktor/src/commonMain/kotlin/org/koin/ktor/di/KtorDIExtension.kt

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,36 @@ package org.koin.ktor.di
1818
import io.ktor.server.application.Application
1919
import io.ktor.server.plugins.di.DependencyKey
2020
import io.ktor.server.plugins.di.dependencies
21+
import io.ktor.server.plugins.di.getBlocking
2122
import io.ktor.util.reflect.TypeInfo
2223
import org.koin.core.instance.ResolutionContext
2324
import org.koin.core.resolution.ResolutionExtension
2425
import org.koin.core.scope.Scope
25-
import kotlinx.coroutines.runBlocking
2626

2727
/**
2828
* Ktor DI Resolver Extension to help Koin resolve Ktor DI objects
2929
*
3030
* @author Arnaud Giuliani
3131
*/
3232
internal class KtorDIExtension(private val application : Application) : ResolutionExtension {
33+
34+
init {
35+
println("[DEBUG] KtorDIExtension init")
36+
}
37+
3338
override val name: String = "ktor-di"
39+
3440
override fun resolve(scope: Scope, instanceContext: ResolutionContext): Any? {
35-
val key = DependencyKey(TypeInfo(instanceContext.clazz), qualifier = instanceContext.qualifier?.value)
36-
// runBlocking is required here because Ktor DI's get() function is suspend
37-
// The blocking call is generally safe as dependency resolution is typically fast and non-blocking
38-
// WARNING: This may cause problems for users as it can impact performance
39-
return runBlocking {
40-
application.dependencies.get(key)
41+
val key = DependencyKey(TypeInfo(instanceContext.clazz), qualifier = instanceContext.qualifier?.value.toString())
42+
43+
println("[DEBUG] KtorDIExtension -> $key")
44+
try {
45+
val value = application.dependencies.getBlocking<Any?>(key)
46+
47+
println("[DEBUG] KtorDIExtension value? $value")
48+
return value
49+
} catch (e: Exception) {
50+
error(e)
4151
}
4252
}
4353
}

projects/ktor/koin-ktor/src/commonMain/kotlin/org/koin/ktor/plugin/KoinIsolatedContextPlugin.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package org.koin.ktor.plugin
1717

1818
import io.ktor.server.application.*
1919
import org.koin.core.KoinApplication
20+
import org.koin.core.KoinKtorApplication
2021
import org.koin.core.annotation.KoinInternalApi
2122

2223
/**
@@ -26,9 +27,10 @@ import org.koin.core.annotation.KoinInternalApi
2627
*
2728
*/
2829
@OptIn(KoinInternalApi::class)
29-
val KoinIsolated = createApplicationPlugin(name = "Koin", createConfiguration = { KoinApplication.init() }) {
30+
val KoinIsolated = createApplicationPlugin(name = "Koin", createConfiguration = { KoinKtorApplication.init() }) {
3031
val koinApplication = setupKoinApplication()
3132
setupMonitoring(koinApplication)
3233
setupKoinScope(koinApplication)
34+
koinApplication.ktorApplication = application
3335
koinApplication.koin.logger.info("Koin is using Ktor isolated context")
3436
}

projects/ktor/koin-ktor/src/commonMain/kotlin/org/koin/ktor/plugin/KoinPlugin.kt

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
@file:OptIn(KoinInternalApi::class)
17+
1618
package org.koin.ktor.plugin
1719

1820
import io.ktor.server.application.Application
@@ -27,14 +29,14 @@ import io.ktor.server.application.pluginOrNull
2729
import io.ktor.util.AttributeKey
2830
import org.koin.core.Koin
2931
import org.koin.core.KoinApplication
32+
import org.koin.core.KoinKtorApplication
3033
import org.koin.core.annotation.KoinInternalApi
3134
import org.koin.core.context.startKoin
3235
import org.koin.core.context.stopKoin
3336
import org.koin.core.module.Module
3437
import org.koin.core.scope.Scope
3538
import org.koin.dsl.KoinAppDeclaration
3639
import org.koin.dsl.ModuleDeclaration
37-
import org.koin.ktor.di.KtorDIExtension
3840
import org.koin.mp.KoinPlatformTools
3941

4042
/**
@@ -47,23 +49,27 @@ import org.koin.mp.KoinPlatformTools
4749
*
4850
*/
4951
val Koin =
50-
createApplicationPlugin(name = "Koin", createConfiguration = { KoinApplication.init() }) {
52+
createApplicationPlugin(
53+
name = "Koin",
54+
createConfiguration = { KoinKtorApplication.init() })
55+
{
5156
val koinApplication = setupKoinApplication()
5257
KoinPlatformTools.defaultContext().getOrNull()?.let { stopKoin() } // for ktor auto-reload
5358
startKoin(koinApplication)
59+
60+
koinApplication.onPostStart()
61+
5462
setupMonitoring(koinApplication)
5563
setupKoinScope(koinApplication)
5664
}
5765

58-
@OptIn(KoinInternalApi::class)
59-
internal fun PluginBuilder<KoinApplication>.setupKoinApplication(): KoinApplication {
66+
internal fun PluginBuilder<KoinKtorApplication>.setupKoinApplication(): KoinKtorApplication {
6067
val koinApplication = pluginConfig
6168
koinApplication.createEagerInstances()
62-
63-
// Register KtorDIExtension for Ktor DI integration
64-
koinApplication.koin.resolver.addResolutionExtension(KtorDIExtension(application))
65-
69+
70+
koinApplication.ktorApplication = application
6671
application.setKoinApplication(koinApplication)
72+
6773
return koinApplication
6874
}
6975

@@ -75,7 +81,7 @@ fun Application.setKoinApplication(koinApplication: KoinApplication) {
7581
attributes.put(KOIN_ATTRIBUTE_KEY, koinApplication.koin)
7682
}
7783

78-
internal fun PluginBuilder<KoinApplication>.setupMonitoring(koinApplication: KoinApplication) {
84+
internal fun PluginBuilder<KoinKtorApplication>.setupMonitoring(koinApplication: KoinKtorApplication) {
7985
val monitor = application.monitor
8086
monitor.raise(KoinApplicationStarted, koinApplication)
8187
monitor.subscribe(ApplicationStopping) {
@@ -85,7 +91,7 @@ internal fun PluginBuilder<KoinApplication>.setupMonitoring(koinApplication: Koi
8591
}
8692
}
8793

88-
internal fun PluginBuilder<KoinApplication>.setupKoinScope(koinApplication: KoinApplication) {
94+
internal fun PluginBuilder<KoinKtorApplication>.setupKoinScope(koinApplication: KoinKtorApplication) {
8995
// Scope Handling
9096
on(CallSetup) { call ->
9197
val scopeComponent = RequestScope(koinApplication.koin, call)

projects/ktor/koin-ktor/src/commonMain/resources/META-INF/services/io.ktor.server.plugins.di.DependencyMapExtension

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)