Kotlin / kotlinx.coroutines

Library support for Kotlin coroutines
Apache License 2.0
13.06k stars 1.85k forks source link

A dependency upgrade for Spring-Boot from V3.1.5 to V3.2.0 causes a CNFE missing MonoKt #3958

Closed SchlauFuchs closed 9 months ago

SchlauFuchs commented 11 months ago

Describe the bug In our project a dependabot automated dependency upgrade is blocked by a ClassNotFoundException during the unit tests runtime:

Exception in thread "DefaultDispatcher-worker-1 @coroutine#2" java.lang.NoClassDefFoundError: kotlinx/coroutines/reactor/MonoKt
    at org.springframework.core.CoroutinesUtils.invokeSuspendingFunction(CoroutinesUtils.java:110)
    at org.springframework.aop.support.AopUtils$KotlinDelegate.invokeSuspendingFunction(AopUtils.java:377)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:352)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:765)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:385)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:765)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:717)
    at ourcompany.api.notices.NoticeService$$SpringCGLIB$$0.createAndDispatchNoticeDocuments(<generated>)
    at ourcompany.api.notices.NoticeService.createAndDispatchNoticeDocuments$default(NoticeService.kt:183)
    at ourcompany.api.notices.NoticeController$createNotice$1.invokeSuspend(NoticeController.kt:99)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
    at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
    at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:103)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
    Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [CoroutineId(2), "coroutine#2":StandaloneCoroutine{Cancelling}@6c8b64e6, Dispatchers.IO]
Caused by: java.lang.ClassNotFoundException: kotlinx.coroutines.reactor.MonoKt
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
    ... 23 more

What happened? What should have happened instead?

Nothing but upgrading the Spring Boot dependencies version from 3.1.5 to 3.2.0 happened and this exception was not expected.

Here is our version.toml file at this stage:

[versions]
aws = "1.12.599"
hibernate = "6.4.0.Final"
jackson = "2.15.2"
kotlin = "1.9.21"
ktlint = "1.0.1"
spring-boot = "3.2.0"
spring-security = "6.0.3"

[libraries]
apache-commons-text = "org.apache.commons:commons-text:1.11.0"
apache-httpclient = "org.apache.httpcomponents:httpclient:4.5.14"
apache-logging-log4j = "org.apache.logging.log4j:log4j-core:2.17.1"
apache-pdfbox = "org.apache.pdfbox:pdfbox:3.0.0"
apache-tika = "org.apache.tika:tika-core:2.9.0"
aws-secretsmanager = { module = "com.amazonaws:aws-java-sdk-secretsmanager", version.ref = "aws" }
aws-ses = { module = "com.amazonaws:aws-java-sdk-ses", version.ref = "aws" }
aws-sts = { module = "com.amazonaws:aws-java-sdk-sts", version.ref = "aws" }
caffeine = "com.github.ben-manes.caffeine:caffeine:3.1.5"
hamcrest = "org.hamcrest:hamcrest-library:2.2"
hibernate-community-dialects = { module = "org.hibernate:hibernate-community-dialects", version.ref = "hibernate" }
hibernate-core = { module = "org.hibernate.orm:hibernate-core", version.ref = "hibernate" }
hibernate-jpamodelgen = { module = "org.hibernate:hibernate-jpamodelgen", version.ref = "hibernate" }
informix-jdbc = "com.ibm.informix:jdbc:4.50.10"
jackson-datatype-jdk8 = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jdk8", version.ref = "jackson" }
jackson-datatype-jsr310 = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310", version.ref = "jackson" }
jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" }
javax-mail = "com.sun.mail:javax.mail:1.6.2"
jetbrains-annotations = "org.jetbrains:annotations:24.1.0"
kotlin-allopen-compiler = { module = "org.jetbrains.kotlin:kotlin-allopen-compiler-plugin-embeddable", version.ref = "kotlin" }
kotlin-annotation-processing-gradle = { module = "org.jetbrains.kotlin:kotlin-annotation-processing-gradle", version.ref = "kotlin" }
kotlin-noarg = { module = "org.jetbrains.kotlin:kotlin-noarg", version.ref = "kotlin" }
kotlin-noarg-compiler = { module = "org.jetbrains.kotlin:kotlin-noarg-compiler-plugin-embeddable", version.ref = "kotlin" }
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
kotlin-scripting-compiler = { module = "org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable", version.ref = "kotlin" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
kotlin-stdlib-common = { module = "org.jetbrains.kotlin:kotlin-stdlib-common", version.ref = "kotlin" }
kotlin-stdlib-jdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlinx-coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"
kotlinx-serialization-json = "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0"
kotest = "io.kotest:kotest-runner-junit5:5.8.0"
ktlint = { module = "com.pinterest:ktlint", version.ref = "ktlint" }
ktlint-reporter-baseline = { module = "com.pinterest.ktlint:ktlint-reporter-baseline", version.ref = "ktlint" }
ktlint-ruleset-standard = { module = "com.pinterest.ktlint:ktlint-ruleset-standard", version.ref = "ktlint" }
lombok-annotations = "de.lars-sh:lombok-annotations:1.18.30"
pdfcompare = "de.redsix:pdfcompare:1.1.62"
split-client = "io.split.client:java-client:4.10.1"
spring-boot-starter-actuator = { module = "org.springframework.boot:spring-boot-starter-actuator", version.ref = "spring-boot" }
spring-boot-starter-cache = { module = "org.springframework.boot:spring-boot-starter-cache", version.ref = "spring-boot" }
spring-boot-starter-data-jpa = { module = "org.springframework.boot:spring-boot-starter-data-jpa", version.ref = "spring-boot" }
spring-boot-starter-data-rest = { module = "org.springframework.boot:spring-boot-starter-data-rest", version.ref = "spring-boot" }
spring-boot-starter-jdbc = { module = "org.springframework.boot:spring-boot-starter-jdbc", version.ref = "spring-boot" }
spring-boot-starter-oauth2-client = { module = "org.springframework.boot:spring-boot-starter-oauth2-client", version.ref = "spring-boot" }
spring-boot-starter-oauth2-resource-server = { module = "org.springframework.boot:spring-boot-starter-oauth2-resource-server", version.ref = "spring-boot" }
spring-boot-starter-security = { module = "org.springframework.boot:spring-boot-starter-security", version.ref = "spring-boot" }
spring-boot-starter-test = { module = "org.springframework.boot:spring-boot-starter-test", version.ref = "spring-boot" }
spring-boot-starter-validation = { module = "org.springframework.boot:spring-boot-starter-validation", version.ref = "spring-boot" }
spring-boot-starter-web = { module = "org.springframework.boot:spring-boot-starter-web", version.ref = "spring-boot" }
spring-boot-starter-webflux = { module = "org.springframework.boot:spring-boot-starter-webflux", version.ref = "spring-boot" }
spring-retry = "org.springframework.retry:spring-retry:2.0.4"
spring-security-oauth2-jose = { module = "org.springframework.security:spring-security-oauth2-jose", version.ref = "spring-security" }
spring-security-oauth2-resource-server = { module = "org.springframework.security:spring-security-oauth2-resource-server", version.ref = "spring-security" }
spring-security-test = { module = "org.springframework.security:spring-security-test", version.ref = "spring-security" }
jakarta-annotation-api = { module = "jakarta.annotation:jakarta.annotation-api", version = "2.1.1" }
[plugins]
flyway = "org.flywaydb.flyway:9.21.1"
hibernate = "org.hibernate.orm:6.2.7.Final"
kotlin-allopen = { id = "org.jetbrains.kotlin.plugin.allopen", version.ref = "kotlin" }
kotlin-jpa = { id = "org.jetbrains.kotlin.plugin.jpa", version.ref = "kotlin" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
kotlin-noarg = { id = "org.jetbrains.kotlin.plugin.noarg", version.ref = "kotlin" }
kotlin-spring = { id = "org.jetbrains.kotlin.plugin.spring", version.ref = "kotlin" }
ktlint = "org.jlleitschuh.gradle.ktlint:11.6.0"
ktlint-idea = "org.jlleitschuh.gradle.ktlint-idea:11.6.0"
openapi-generator = "org.openapi.generator:7.0.1"
spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot" }

[bundles]
spring-boot = [
    "spring-boot-starter-cache",
    "spring-boot-starter-actuator",
    "spring-boot-starter-data-jpa",
    "spring-boot-starter-data-rest",
    "spring-boot-starter-jdbc",
    "spring-boot-starter-validation",
    "spring-boot-starter-web",
    "spring-boot-starter-webflux",
    "jakarta-annotation-api"
]
auth = [
    "spring-security-oauth2-jose",
    "spring-security-oauth2-resource-server",
    "spring-boot-starter-security",
    "spring-boot-starter-oauth2-client",
    "spring-boot-starter-oauth2-resource-server",
    "caffeine"
]

spring-boot-test = [
    "spring-boot-starter-test",
]

Provide a Reproducer Sorry, I don't know enough about coroutines and their interaction with Spring-Boot. The code that is causing the exception looks like this:

    @Transactional(rollbackFor = []) 
    suspend fun createAndDispatchNoticeDocuments(
        noticeId: Int,
        recipients: List<User>,
        firmRecipients: List<Corporation>,
        noticeType: NoticeType,
        userId: String,
        async: Boolean? = false
    ): Notice {
// [stuff happens here]
    }

The exception does not happen in the method but in the AOP part between the controller and the called service, which calls it like this:

            CoroutineScope(Dispatchers.IO).launch {
                noticeService.createAndDispatchNoticeDocuments(
                    notice.id,
                    firmUserRecipients,
                    activeFirmRecipients,
                    noticeType,
                    userId
                )
            }

The main question is, what happened between SpringBoot 3.1.5 and 3.2.0 that breaks this?

dkhalanskyjb commented 11 months ago

The main question is, what happened between SpringBoot 3.1.5 and 3.2.0 that breaks this?

Why are you asking what happened in a Spring Boot update in the coroutines repository? Why not report it to Spring instead?

Maybe you could work around this by adding a dependency to the reactor artifact (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-reactor/kotlinx.coroutines.reactor/, kotlinx-coroutines-reactor)? It does provide the kotlinx/coroutines/reactor/MonoKt class.

SchlauFuchs commented 11 months ago

Because the class that is suddenly not found is a Kotlinx coroutines class, and why should I expect the spring framework people to respond to the problem when it is caused by a kotlin/kotlinx keyword. But thank you for the suggestion, I will try that when I arrive at work.

On Fri, 1 Dec 2023 at 03:17, Dmitry Khalanskiy @.***> wrote:

The main question is, what happened between SpringBoot 3.1.5 and 3.2.0 that breaks this?

Why are you asking what happened in a Spring Boot update in the coroutines repository? Why not report it to Spring instead?

Maybe you could work around this by adding a dependency to the reactor artifact ( https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-reactor/kotlinx.coroutines.reactor/, kotlinx-coroutines-reactor)? It does provide the kotlinx/coroutines/reactor/MonoKt class.

β€” Reply to this email directly, view it on GitHub https://github.com/Kotlin/kotlinx.coroutines/issues/3958#issuecomment-1833865483, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAHK5HMCSHN4P3Y7XBPNUQLYHCIP7AVCNFSM6AAAAABAAFYGH2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQMZTHA3DKNBYGM . You are receiving this because you authored the thread.Message ID: @.***>

ryanelliott-wk commented 10 months ago

@SchlauFuchs πŸ‘‹ were you able to resolve your issue? Did you report it to Spring?

SchlauFuchs commented 10 months ago

Hi, Adding the missing library to the dependencies worked to pass the exception, but then we run into problems with transactions across parallel processes, currently working on replacing the coroutines with spring Async annotated methods

On Sat, 23 Dec 2023, 10:25 Ryan Elliott, @.***> wrote:

@SchlauFuchs https://github.com/SchlauFuchs πŸ‘‹ were you able to resolve your issue? Did you report it to Spring?

β€” Reply to this email directly, view it on GitHub https://github.com/Kotlin/kotlinx.coroutines/issues/3958#issuecomment-1868078263, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAHK5HNH5LCT3HUJNEIBXZTYKX3DJAVCNFSM6AAAAABAAFYGH2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQNRYGA3TQMRWGM . You are receiving this because you were mentioned.Message ID: @.***>