Koin: Koin cannot find beans on Ktor when autoreload is active

Created on 13 Mar 2018  路  16Comments  路  Source: InsertKoinIO/koin

Steps to reproduce :

  • Checkout the ktor-hellowebapp from the samples
  • Add these lines in the deployment block of application.conf
        autoreload = true
        watch = [ sample ]
  • Run the app

Stacktrace :

> Task :run
(KOIN) :: [INFO] :: [context] create
(KOIN) :: [INFO] :: [module] declare Bean[class=org.koin.sample.BusinessService]
(KOIN) :: [INFO] :: [modules] loaded 1 definitions
(KOIN) :: [INFO] :: [properties] load koin.properties
(KOIN) :: [INFO] :: [properties] load extras properties : 2
(KOIN) :: [DEBUG] :: [Property] add properties 2
2018-03-13 10:59:17.640 [main] TRACE Application - {
    # application.conf @ file:/Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/resources/main/application.conf: 8
    "application" : {
        # application.conf @ file:/Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/resources/main/application.conf: 9
        "modules" : [
            # application.conf @ file:/Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/resources/main/application.conf: 10
            "org.koin.sample.KoinApplicationKt.main",
            # application.conf @ file:/Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/resources/main/application.conf: 11
            "org.koin.sample.JobRoutesKt.jobRoutes"
        ]
    },
    # application.conf @ file:/Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/resources/main/application.conf: 2
    "deployment" : {
        # application.conf @ file:/Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/resources/main/application.conf: 4
        "autoreload" : true,
        # application.conf @ file:/Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/resources/main/application.conf: 3
        "port" : 8080,
        # application.conf @ file:/Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/resources/main/application.conf: 5
        "watch" : [
            # application.conf @ file:/Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/resources/main/application.conf: 5
            "sample"
        ]
    },
    # Content hidden
    "security" : "***"
}

2018-03-13 10:59:17.740 [main] DEBUG Application - Java Home: /Library/Java/JavaVirtualMachines/jdk1.8.0_152.jdk/Contents/Home
2018-03-13 10:59:17.740 [main] DEBUG Application - Class Loader: sun.misc.Launcher$AppClassLoader@15db9742: [/* removed */]
2018-03-13 10:59:17.757 [main] DEBUG Application - Watching /Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/classes/kotlin/main/org for changes.
2018-03-13 10:59:17.757 [main] DEBUG Application - Watching /Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/resources/main for changes.
2018-03-13 10:59:17.757 [main] DEBUG Application - Watching /Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/classes/kotlin/main for changes.
2018-03-13 10:59:17.757 [main] DEBUG Application - Watching /Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/classes/kotlin/main/org/koin for changes.
2018-03-13 10:59:17.757 [main] DEBUG Application - Watching /Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/classes/kotlin/main/META-INF for changes.
2018-03-13 10:59:17.757 [main] DEBUG Application - Watching /Users/tarek/Sources/github/koin-samples/samples/ktor-hellowebapp/build/classes/kotlin/main/org/koin/sample for changes.
2018-03-13 10:59:18.124 [main] TRACE Application - Application started: io.ktor.application.Application@928763c
2018-03-13 10:59:23.849 [nettyCallPool-4-1] ERROR Application - Unhandled: GET - /bye
org.koin.error.NoBeanDefFoundException: No definition found to resolve type 'org.koin.sample.BusinessService'. Check your module definition
        at org.koin.KoinContext.getVisibleBeanDefinition(KoinContext.kt:119)
        at org.koin.KoinContext.resolveInstance(KoinContext.kt:77)
        at org.koin.sample.JobRoutesKt$jobRoutes$$inlined$inject$2.invoke(KtorApplicationExt.kt:82)
        at kotlin.SynchronizedLazyImpl.getValue(Lazy.kt:131)
        at org.koin.sample.JobRoutesKt$jobRoutes$1$4.doResume(JobRoutes.kt:43)
        at org.koin.sample.JobRoutesKt$jobRoutes$1$4.invoke(JobRoutes.kt)
        at org.koin.sample.JobRoutesKt$jobRoutes$1$4.invoke(JobRoutes.kt)
        at io.ktor.pipeline.PipelineContext.proceed(PipelineContext.kt:49)
        at io.ktor.pipeline.Pipeline.execute(Pipeline.kt:22)
        at io.ktor.routing.Routing.executeResult(Routing.kt:93)
        at io.ktor.routing.Routing.interceptor(Routing.kt:19)
        at io.ktor.routing.Routing$Feature$install$1.doResume(Routing.kt:60)
        at io.ktor.routing.Routing$Feature$install$1.invoke(Routing.kt)
        at io.ktor.routing.Routing$Feature$install$1.invoke(Routing.kt:45)
        at io.ktor.pipeline.PipelineContext.proceed(PipelineContext.kt:49)
        at io.ktor.features.CallLogging$Feature$install$1.doResume(CallLogging.kt:69)
        at io.ktor.features.CallLogging$Feature$install$1.invoke(CallLogging.kt)
        at io.ktor.features.CallLogging$Feature$install$1.invoke(CallLogging.kt:61)
        at io.ktor.pipeline.PipelineContext.proceed(PipelineContext.kt:49)
        at io.ktor.pipeline.Pipeline.execute(Pipeline.kt:22)
        at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.doResume(DefaultEnginePipeline.kt:66)
        at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invoke(DefaultEnginePipeline.kt)
        at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invoke(DefaultEnginePipeline.kt)
        at io.ktor.pipeline.PipelineContext.proceed(PipelineContext.kt:49)
        at io.ktor.pipeline.Pipeline.execute(Pipeline.kt:22)
        at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.doResume(NettyApplicationCallHandler.kt:27)
        at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invoke(NettyApplicationCallHandler.kt)
        at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invoke(NettyApplicationCallHandler.kt:10)
        at kotlinx.coroutines.experimental.intrinsics.UndispatchedKt.startCoroutineUndispatched(Undispatched.kt:44)
        at kotlinx.coroutines.experimental.CoroutineStart.invoke(CoroutineStart.kt:113)
        at kotlinx.coroutines.experimental.AbstractCoroutine.start(AbstractCoroutine.kt:161)
        at kotlinx.coroutines.experimental.BuildersKt.launch(Builders.kt:68)
        at kotlinx.coroutines.experimental.BuildersKt.launch$default(Builders.kt:61)
        at io.ktor.server.netty.NettyApplicationCallHandler.handleRequest(NettyApplicationCallHandler.kt:22)
        at io.ktor.server.netty.NettyApplicationCallHandler.channelRead(NettyApplicationCallHandler.kt:16)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
        at io.netty.channel.AbstractChannelHandlerContext.access$600(AbstractChannelHandlerContext.java:38)
        at io.netty.channel.AbstractChannelHandlerContext$7.run(AbstractChannelHandlerContext.java:353)
        at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463)
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:886)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.lang.Thread.run(Thread.java:748)
ktor accepted issue

Most helpful comment

Hello,

Ok. let us check with autoreload.

All 16 comments

Hello,

Ok. let us check with autoreload.

I pushed an update on https://github.com/Ekito/koin-samples

I can run the actual app :/ and it reloads

Give a try ?

Did you try calling /bye ?
/ works fine but all the other routes don't seem to work
(It is also maybe worth mentioning that I run the app using the gradle 'application' plugin)

@Baresse is it the good way of working?

@tokou yes, tests are ok. But can't run any other route than "/"

We will check that! 馃憤

We got it! It was a misconfiguration in application.conf. Watched module didn't exist.

Check https://github.com/Ekito/koin-samples/blob/master/samples/ktor-hellowebapp/src/main/resources/application.conf

Cheers.

@arnaudgiuliani The fix that was added just turns off auto-reload.

I can confirm this still exists. When engine reloading occurs, the BeanRegistry is still populated, but all calls to it fail.

As part of the engine reloading, it looks like the application and possibly classloader is destroyed: https://github.com/ktorio/ktor/blob/37cbb5c59e3c5ae26fd7d2185a26f6e23e076802/ktor-server/ktor-server-host-common/src/io/ktor/server/engine/ApplicationEngineEnvironmentReloading.kt#L171

The best workaround I've found is to do this, when the app is being restarted (as a ktor module):

kotlin (StandAloneContext.koinContext as KoinContext).beanRegistry.clear() loadKoinModules(listOf(modulesHere))

Alternatively, you could start koin inside of the ktor module, which is cleanly created every time, but you lose out on property resolution, which is only available(in my case) in the main method.

hello,

let us check that. You can also propose anything in the 1.0.0 branches.

You can check that this problem is still there in koin-ktor:1.0.0-beta-1?

Confirmed it still happens on beta-1. Switching from 0.9.2 to 1.0.0beta-1 was trivial though, so that's good.

(KOIN)::[err] Error while resolving instance for class 'JacksonObjectMapperService' - error: org.koin.error.NoBeanDefFoundException: No definition found to resolve type 'com.c2fo.tpf.service.common.Jackson$
bjectMapperService'. Check your module definition                                                                                                                                                             
Exception in thread "main" java.lang.reflect.InvocationTargetException                                                                                                                                        
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)                                                                                                                                        
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at kotlin.reflect.jvm.internal.FunctionCaller$Method.callMethod(FunctionCaller.kt:98)
        at kotlin.reflect.jvm.internal.FunctionCaller$StaticMethod.call(FunctionCaller.kt:108)
        at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:107)
        at kotlin.reflect.jvm.internal.KCallableImpl.callDefaultMethod(KCallableImpl.kt:149)
        at kotlin.reflect.jvm.internal.KCallableImpl.callBy(KCallableImpl.kt:111)
        at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.callFunctionWithInjection(ApplicationEngineEnvironmentReloading.kt:330)                                                               
        at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.executeModuleFunction(ApplicationEngineEnvironmentReloading.kt:286)                                                                   
        at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.instantiateAndConfigureApplication(ApplicationEngineEnvironmentReloading.kt:263)                                                      
        at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.createApplication(ApplicationEngineEnvironmentReloading.kt:120)                                                                       
        at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.start(ApplicationEngineEnvironmentReloading.kt:235)                                                                                   
        at io.ktor.server.netty.NettyApplicationEngine.start(NettyApplicationEngine.kt:85)
        at io.ktor.server.netty.NettyApplicationEngine.start(NettyApplicationEngine.kt:16)
        at io.ktor.server.engine.ApplicationEngine$DefaultImpls.start$default(ApplicationEngine.kt:46)
        at xxx.yyy.zzz.main(ApiApplicationV2.kt:101)
Caused by: org.koin.error.NoBeanDefFoundException: No definition found to resolve type 'xxx.yyy.zzz.JacksonObjectMapperService'. Check your module definition                                
        at org.koin.core.bean.BeanRegistry.getVisibleBean(BeanRegistry.kt:121)
        at org.koin.core.KoinContext.resolveInstance(KoinContext.kt:109)

I think it has to do with the classes getting evicted and reloaded as part of the dev environment being re-instantiated.

Calling beanRegistry.search(clazz) returns nothing, despite there being 17 beans defined, with expected class values.

I like the idea of Koin having property resolvers, so I'm hesitant to suggest this, but if you move the Koin start inside of Ktor's modules, you're fine (though I would guess there is a memory leak, since stale definitions aren't cleared).

I like the idea of Koin having property resolvers, so I'm hesitant to suggest this, but if you move the Koin start inside of Ktor's modules, you're fine (though I would guess there is a memory leak, since stale definitions aren't cleared).

We'll check in this direction thanks.

Don't hesitate to propose any solution with a PR if you want.

So far the only workaround that worked for me is

fun Application.main() {
    ...
    StandAloneContext.closeKoin()
    StandAloneContext.startKoin(arrayListOf(KoinModule))
}

A proposal can be to add a starter method for Ktor, which does the 2 things: close+start to be sure that Ktor can reload it.

I wrote an extension for Application: the installKoin() function, that does stop/start Koin. Work in my simple demo app below.

````
fun Application.main() {
// Install Ktor features
install(DefaultHeaders)
install(CallLogging)
installKoin(listOf(helloAppModule), logger = SLF4JLogger())

// Lazy inject HelloService
val service by inject<HelloService>()

// Routing section
routing {
    get("/hello") {
        call.respondText(service.sayHello())
    }

    v1()
}

}
```

Available in Koin 1.0.0-beta-6. koin-logger-slf4j project is in redindexing from jfrog.

And then, no need to start Koin from outside Ktor. Works also for unit test 馃憤

koin-ktor is fully available in 1.0.0-beta-6 with koin-logger-slf4j

Was this page helpful?
0 / 5 - 0 ratings

Related issues

iRYO400 picture iRYO400  路  3Comments

caleb-allen picture caleb-allen  路  4Comments

luna-vulpo picture luna-vulpo  路  4Comments

LukasAnda picture LukasAnda  路  3Comments

AHarazim picture AHarazim  路  3Comments