ClassNotFoundException when using REST clients inside Kotlin coroutines after Quarkus version upgrade
I am trying to upgrade from Quarkus version 3.8.4 -> 3.15.2 and I’m facing the below issue when using coroutines. Please refer to code examples below:
The below shows a method hello() being called which invokes a REST client call and prints the output as a string (This is done within an async Kotlin coroutine block):
@Inject
@RestClient
lateinit var myRemoteService: MyRemoteService
@GET
@Produces(MediaType.TEXT_PLAIN)
suspend fun hello(): String {
println(doSomething().toString())
return "Hello from Quarkus REST"
}
suspend fun doSomething(): Set<MyRemoteService.Extension>? {
var restClientExtensions: Set<MyRemoteService.Extension>? = null
withContext(Dispatchers.IO) {
restClientExtensions = myRemoteService.getExtensionsById("io.quarkus:quarkus-rest-client")
}
return restClientExtensions
}
This is how some of my functions were written when I was using Quarkus v3.8.4
I’ve now attempted to upgrade to 3.15.2 (Latest LTS version) and I’m getting this error on my code:
2024-12-13 12:01:05,768 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (vert.x-eventloop-thread-1) HTTP Request to /hello failed, error id: d40a9779-93d1-4b50-ab05-d3adb047da96-3: jakarta.enterprise.inject.CreationException: java.lang.ClassNotFoundException: org.acme.MyRemoteService
at org.acme.MyRemoteService$$CDIWrapper_Bean.create(Unknown Source)
at org.acme.MyRemoteService$$CDIWrapper_Bean.create(Unknown Source)
at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:119)
at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:38)
at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:35)
at io.quarkus.arc.generator.Default_jakarta_enterprise_context_ApplicationScoped_ContextInstances.c1(Unknown Source)
at io.quarkus.arc.generator.Default_jakarta_enterprise_context_ApplicationScoped_ContextInstances.computeIfAbsent(Unknown Source)
at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:35)
at io.quarkus.arc.impl.ClientProxies.getApplicationScopedDelegate(ClientProxies.java:21)
at org.acme.MyRemoteService$$CDIWrapper_ClientProxy.arc$delegate(Unknown Source)
at org.acme.MyRemoteService$$CDIWrapper_ClientProxy.getExtensionsById(Unknown Source)
at org.acme.GreetingResource$doSomething$2.invokeSuspend(GreetingResource.kt:32)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:101)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:113)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:89)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:589)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:823)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:720)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:707)
Caused by: java.lang.ClassNotFoundException: org.acme.MyRemoteService
at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:542)
at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:517)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:467)
at org.acme.MyRemoteService$$CDIWrapper.<init>(Unknown Source)
at org.acme.MyRemoteService$$CDIWrapper_Bean.doCreate(Unknown Source)
... 20 more
If I re-factor the same code, now without using suspend modifiers or Kotlin coroutines like below, then it works again:
@GET
@Produces(MediaType.TEXT_PLAIN)
fun hello(): String {
println(doSomething().toString())
return "Hello from Quarkus REST"
}
fun doSomething(): Set<MyRemoteService.Extension>? {
var restClientExtensions: Set<MyRemoteService.Extension>? = null
//withContext(Dispatchers.IO) { // Commented out
restClientExtensions = myRemoteService.getExtensionsById("io.quarkus:quarkus-rest-client")
//}
return restClientExtensions
}
Any insights as to why this is happening would be really helpful. The code which is initially displayed worked on the prior Quarkus version and on the updated code it isn’t. Why does using Kotlin coroutines (suspend) with REST clients result in a ClassNotFoundException in Quarkus 3.15.2?
Here’s some further information which you may require:
-
Note that quarkus had a dependency restructure for this version upgrade too (RESTEasy-Reactive being rebranded to REST) – [Reference: https://github.com/quarkusio/quarkus/wiki/Migration-Guide-3.9]
-
Detailed version upgrade information (From pom.xml):
quarkus platform version 3.8.4 to 3.15.2,
kotlin version 1.9.21 to 2.0.21,
compiler plugin version 3.11.0 to 3.12.1
1