Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ internal suspend fun executeModuleFunction(
?: throw ReloadingException("Module function cannot be found for the fully qualified name '$fqName'")

val staticFunctions = clazz.methods
.filter { it.name == functionName && Modifier.isStatic(it.modifiers) }
.filter { Modifier.isStatic(it.modifiers) }
.mapNotNull { it.kotlinFunction }
.filter { it.isApplicableFunction() }
.filter { it.name == functionName && it.isApplicableFunction() }

staticFunctions.bestFunction()?.let { moduleFunction ->
if (moduleFunction.parameters.none { it.kind == KParameter.Kind.INSTANCE }) {
Expand Down Expand Up @@ -107,23 +107,23 @@ private suspend fun <R> callFunctionWithInjection(
parameter.kind == KParameter.Kind.INSTANCE -> instance
isApplicationEnvironment(parameter) -> application.environment
isApplication(parameter) -> application
parameter.type.toString().contains("Application") -> {
// It is possible that type is okay, but classloader is not
val classLoader = (parameter.type.javaType as? Class<*>)?.classLoader
throw IllegalArgumentException(
"Parameter type ${parameter.type}:{$classLoader} is not supported." +
"Application is loaded as " +
"$ApplicationClassInstance:{${ApplicationClassInstance.classLoader}}"
)
}

else -> {
val injectedValue = runCatching {
moduleInjector.resolveParameter(application, parameter)
}
val injectedValue = runCatching { moduleInjector.resolveParameter(application, parameter) }
when {
injectedValue.isSuccess -> injectedValue.getOrThrow()
parameter.isOptional -> return@mapNotNull null // skip
parameter.type.isMarkedNullable -> null // value = null
// This check should go last
parameter.type.toString().contains("Application") -> {
// It is possible that type is okay, but classloader is not
val classLoader = (parameter.type.javaType as? Class<*>)?.classLoader
throw IllegalArgumentException(
"Parameter type ${parameter.type}:{$classLoader} is not supported. " +
"Application is loaded as " +
"$ApplicationClassInstance:{${ApplicationClassInstance.classLoader}}"
)
}
else -> throw IllegalArgumentException(
"Failed to inject parameter `${parameter.name ?: "<receiver>"}: ${parameter.type}` " +
"in module function `$entryPoint`",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

package io.ktor.tests.hosts

import com.typesafe.config.*
import com.typesafe.config.ConfigFactory
import io.ktor.client.request.*
import io.ktor.events.*
import io.ktor.server.application.*
Expand All @@ -15,11 +15,16 @@ import io.ktor.server.engine.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.testing.*
import io.ktor.tests.hosts.EmbeddedServerReloadingTests.Companion.addLoadedModule
import io.ktor.util.*
import kotlinx.coroutines.*
import org.slf4j.helpers.*
import kotlin.reflect.*
import kotlin.reflect.jvm.*
import kotlinx.coroutines.Job
import kotlinx.coroutines.runBlocking
import org.slf4j.helpers.NOPLogger
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KFunction0
import kotlin.reflect.jvm.javaMethod
import kotlin.reflect.jvm.jvmName
import kotlin.test.*

class EmbeddedServerReloadingTests {
Expand Down Expand Up @@ -391,7 +396,9 @@ class EmbeddedServerReloadingTests {
mapOf(
"ktor.deployment.environment" to "test",
"ktor.application.modules" to listOf(
EmbeddedServerReloadingTests::class.jvmName + "Kt.topLevelWithDefaultArg"
Application::defaultArgBoolean.fqName,
Application::defaultArgContainingApplicationWord.fqName,
Application::defaultArgInline.fqName,
)
)
)
Expand All @@ -405,7 +412,14 @@ class EmbeddedServerReloadingTests {
server.start()
val application = server.application
assertNotNull(application)
assertEquals("topLevelWithDefaultArg", application.attributes[TestKey])
assertEquals(
setOf(
"defaultArgBoolean",
"defaultArgContainingApplicationWord",
"defaultArgInline",
),
application.loadedModules,
)
server.stop()
}

Expand Down Expand Up @@ -512,6 +526,14 @@ class EmbeddedServerReloadingTests {
companion object {
val TestKey = AttributeKey<String>("test-key")
val TestKey2 = AttributeKey<String>("test-key2")
private val LoadedModulesKey = AttributeKey<MutableSet<String>>("loaded-modules")

val Application.loadedModules: Set<String>
get() = attributes.getOrNull(LoadedModulesKey).orEmpty()

fun Application.addLoadedModule(name: String) {
attributes.computeIfAbsent(LoadedModulesKey) { mutableSetOf() }.add(name)
}

private val KFunction<*>.fqName: String
get() = javaMethod!!.declaringClass.name + "." + name
Expand Down Expand Up @@ -652,8 +674,19 @@ fun topLevelFunction() {
error("Shouldn't be invoked")
}

fun Application.topLevelWithDefaultArg(testing: Boolean = false) {
attributes.put(EmbeddedServerReloadingTests.TestKey, "topLevelWithDefaultArg")
fun Application.defaultArgBoolean(testing: Boolean = false) {
addLoadedModule("defaultArgBoolean")
}

fun Application.defaultArgContainingApplicationWord(configure: Application.() -> Unit = {}) {
addLoadedModule("defaultArgContainingApplicationWord")
}

@JvmInline
value class InstanceId(val value: String)

fun Application.defaultArgInline(id: InstanceId = InstanceId("default")) {
addLoadedModule("defaultArgInline")
}

@JvmOverloads
Expand Down