I have a RouteBuilder object that dynamically conf...
# ktor
r
I have a RouteBuilder object that dynamically configures all the routes for my application, given a list of controller classes. However, when I run the project without the gradle shadow plugin, the object doesn't seem to cross into the embeddedServer context. The relevant code snippet and resulting log output is pasted below:
Copy code
<http://log.info|log.info>("RouteTypeMap size: ")
		<http://log.info|log.info>(RouteBuilder.routeTypeMap.size.toString())

		embeddedServer(
				Netty
			, port = 11111
		) {
			<http://log.info|log.info>("RouteTypeMap size in configuration block: ")
			<http://log.info|log.info>(RouteBuilder.routeTypeMap.size.toString())
Log:
Copy code
RouteTypeMap size:
870
RouteTypeMap size in configuration block:
0
My knowledge of kotlin isn't deep enough to really have a a clue as to why this is happening 😅
a
Could you please share the
RouteBuilder
definition?
r
object RouteBuilder { private val log = LoggerFactory.getLogger(this.javaClass) // private val instancesInternal: MutableMap<Route, KClass<*>> = mutableMapOf() // val instances: Map<Route, KClass<*>> // get() = instancesInternal.toMap() private val routeTypeMapPrivate = mutableMapOf<String, RouteType>() val routeTypeMap: Map<String, RouteType> get() = routeTypeMapPrivate // async upload private val asyncUploadRouteMapPrivate = mutableMapOf<String, Triple<Route, KClass<*>, KFunction2<Any, AsyncUploadRequest, AsyncUploadResponse>>>() val asyncUploadRouteMap: Map<String, Triple<Route, KClass<*>, KFunction2<Any, AsyncUploadRequest, AsyncUploadResponse>>> get() = asyncUploadRouteMapPrivate // blocking upload private val blockingUploadRouteMapPrivate = mutableMapOf<String, Triple<Route, KClass<*>, KFunction2<Any, BlockingUploadRequest, BlockingUploadResponse>>>() val blockingUploadRouteMap: Map<String, Triple<Route, KClass<*>, KFunction2<Any, BlockingUploadRequest, BlockingUploadResponse>>> get() = blockingUploadRouteMapPrivate // blocking download private val blockingDownloadRouteMapPrivate = mutableMapOf<String, Triple<Route, KClass<*>, KFunction2<Any, BlockingDownloadRequest, BlockingDownloadResponse>>>() val blockingDownloadRouteMap: Map<String, Triple<Route, KClass<*>, KFunction2<Any, BlockingDownloadRequest, BlockingDownloadResponse>>> get() = blockingDownloadRouteMapPrivate // blocking post private val blockingPostRouteMapPrivate = mutableMapOf<String, Triple<Route, KClass<*>, KFunction2<Any, BlockingPostRequest, BlockingPostResponse>>>() val blockingPostRouteMap: Map<String, Triple<Route, KClass<*>, KFunction2<Any, BlockingPostRequest, BlockingPostResponse>>> get() = blockingPostRouteMapPrivate // async post private val asyncPostRouteMapPrivate = mutableMapOf<String, Triple<Route, KClass<*>, KFunction2<Any, AsyncPostRequest, AsyncPostResponse>>>() val asyncPostRouteMap: Map<String, Triple<Route, KClass<*>, KFunction2<Any, AsyncPostRequest, AsyncPostResponse>>> get() = asyncPostRouteMapPrivate // blocking get private val blockingGetRouteMapPrivate = mutableMapOf<String, Triple<Route, KClass<*>, KFunction2<Any, BlockingGetRequest, BlockingGetResponse>>>() val blockingGetRouteMap: Map<String, Triple<Route, KClass<*>, KFunction2<Any, BlockingGetRequest, BlockingGetResponse>>> get() = blockingGetRouteMapPrivate // async get private val asyncGetRouteMapPrivate = mutableMapOf<String, Triple<Route, KClass<*>, KFunction2<Any, AsyncGetRequest, AsyncGetResponse>>>() val asyncGetRouteMap: Map<String, Triple<Route, KClass<*>, KFunction2<Any, AsyncGetRequest, AsyncGetResponse>>> get() = asyncGetRouteMapPrivate // static get private val staticGetRouteMapPrivate = mutableMapOf<String, Triple<Route, KClass<*>, KFunction2<Any, StaticGetRequest, StaticGetResponse>>>() val staticGetRouteMap: Map<String, Triple<Route, KClass<*>, KFunction2<Any, StaticGetRequest, StaticGetResponse>>> get() = staticGetRouteMapPrivate val routes = mutableMapOf<String, Triple<Route, Any, KFunction2<Any, *, *>>>() private var routeCount = 0 @Suppress("UNCHECKED_CAST") fun setup(controllers: Set<KClass<*>>) { log.info("Setting up controllers: ") log.info(controllers.size.toString()) LulaServer.log.info("Scanning ${controllers.size} controllers for Route Annotations...") val duration = durationOf { controllers.forEach { clazz -> log.info(clazz.simpleName) log.info("Number of functions: " + clazz.functions.size.toString()) clazz.functions.forEach { function -> log.info(function.name) log.info("Number of annotations: " + function.annotations.size.toString()) function.annotations.forEach { annotation -> if (annotation is Route) { log.info("Annotation is route: " + annotation.url) routeTypeMapPrivate.putIfAbsent(annotation.url, annotation.routeType) when (annotation.routeType) { ASYNC_UPLOAD -> { asyncUploadRouteMapPrivate.putIfAbsent( annotation.url, Triple( annotation, clazz, function as KFunction2<Any, AsyncUploadRequest, AsyncUploadResponse>, ) ) } BLOCKING_UPLOAD -> { blockingUploadRouteMapPrivate.putIfAbsent( annotation.url, Triple( annotation, clazz, function as KFunction2<Any, BlockingUploadRequest, BlockingUploadResponse>, ) ) } BLOCKING_DOWNLOAD -> { blockingDownloadRouteMapPrivate.putIfAbsent( annotation.url, Triple( annotation, clazz, function as KFunction2<Any, BlockingDownloadRequest, BlockingDownloadResponse>, ) ) } BLOCKING_POST -> { blockingPostRouteMapPrivate.putIfAbsent( annotation.url, Triple( annotation, clazz, function as KFunction2<Any, BlockingPostRequest, BlockingPostResponse>, ) ) } ASYNC_POST -> { asyncPostRouteMapPrivate.putIfAbsent( annotation.url, Triple( annotation, clazz, function as KFunction2<Any, AsyncPostRequest, AsyncPostResponse>, ) ) } BLOCKING_GET -> { blockingGetRouteMapPrivate.putIfAbsent( annotation.url, Triple( annotation, clazz, function as KFunction2<Any, BlockingGetRequest, BlockingGetResponse>, ) ) } ASYNC_GET -> { asyncGetRouteMapPrivate.putIfAbsent( annotation.url, Triple( annotation, clazz, function as KFunction2<Any, AsyncGetRequest, AsyncGetResponse>, ) ) } STATIC_GET -> { staticGetRouteMapPrivate.putIfAbsent( annotation.url, Triple( annotation, clazz, function as KFunction2<Any, StaticGetRequest, StaticGetResponse>, ) ) } else -> LulaServer.log.error("Unsure how to routify $function") } routeCount++ } } } } }
😱 2
@Aleksei Tirman [JB] I found the root cause of the issue. KTor development mode seems to break reflection by overriding the classloader (https://youtrack.jetbrains.com/issue/KTOR-2306), and as you can see in the wild snippet above, our routes are set up using reflection. If I run the project without development mode, everything works. Do you know if there's a workaround that will still allow us to use reflection to set up the routes? There's about 850 routes, so I don't want to rewrite everything, especially since I am new on the project.
a
@e5l might help.
🙂 1
e
Hey @Raymond Boswel, could you tell me what version of Ktor is in your project?
r
Hi @e5l 🙂 It's currently 1.5.4.
I can try with latest?
e
Sure
r
@e5l, I tried with the latest, but still get classloader issues when I run in development mode (the current symptom is the hibernate classes not matching: Type specified for TypedQuery [za.co.api.model.account.Account] is incompatible with query return type [class za.co.api.model.account.Account])
e
Yep, the development mode will have this issue with reloading. It looks like we need a stop list for packages or class names, could you file an issue?
r
Yes sure 👍 I created https://youtrack.jetbrains.com/issue/KTOR-3543. Let me know if I should add anything. I'm busy with a big rework of our backend, moving to gradle, adding docker etc. But the workflow doesn't really work without hot reloading, so hopefully there's a solution 🤞
@e5l Do you mean that there will be a list that is exposed where the developer can add packages that shouldn't be handled by the overridden class loader? Would those classes then still be included in the auto reload mechanism?
e
Yep, and they can’t be included
r
@e5l Is there a way that the whole application layer can be included so that auto reloading would work for everything? What would I have to change for that to be the case?