Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to use Kotlin value classes as function arguments to functions in @Repository when having dependency to org.springframework:spring-tx on the classpath #31890

Closed
pwestlin opened this issue Dec 22, 2023 · 5 comments
Assignees
Labels
status: duplicate A duplicate of another issue theme: kotlin An issue related to Kotlin support

Comments

@pwestlin
Copy link

pwestlin commented Dec 22, 2023

When upgrading to Spring Boot 3.2.1 (from 3.1.7) we encountered a bug that throws an exception saying "object is not an instance of declaring class".

After a lot of dogging we've found out that the excpeption only occured when we had org.springframework:spring-tx on the classpath.
Acutually, org.springframework:spring-tx is a transitive dependency from org.springframework:spring-jms that we are using.

Environment

Spring Framework Version: 6.1.2 (with spring-boot 3.2.1)
Kotlin Version: 1.9.22
JVM Version: 17

Code

build.gradle.kts

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "3.2.1"
    id("io.spring.dependency-management") version "1.1.4"
    kotlin("jvm") version "1.9.22"
    kotlin("plugin.spring") version "1.9.22"
}

group = "se.lantmateriet.tacodus.orderhanteraremock"
version = "0.0.1-SNAPSHOT"

java {
    sourceCompatibility = JavaVersion.VERSION_17
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
    implementation("org.apache.httpcomponents:httpclient:4.5.14")

    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("io.projectreactor:reactor-test")

    implementation("org.springframework:spring-tx")
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs += "-Xjsr305=strict"
        jvmTarget = "17"
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

Application

@SpringBootApplication
class ValueClassBugApplication

fun main(args: Array<String>) {
    runApplication<ValueClassBugApplication>(*args)
}

@RestController
@RequestMapping(("/foo"))
class FooController(
    private val fooRepository: FooRepository
) {

    @GetMapping("")
    suspend fun foo() {
        fooRepository.doSomething(AValueClass("foo"))
    }
}

//@Service
@Repository
class FooRepository {
    suspend fun doSomething(aValueClass: AValueClass) {
        //
    }
}

@JvmInline
value class AValueClass(val value: String)

Start the application and make a GET request;

curl -i localhost:8080/foo

Expected behavior

Server returns 200

Actual Behavior

The server returns 500 and gets an internal server error with an stacktrace:

2023-12-22T14:52:20.865+01:00 ERROR 1626972 --- [         task-1] a.w.r.e.AbstractErrorWebExceptionHandler : [71ea1fd0-1]  500 Server Error for HTTP GET "/foo"

java.lang.IllegalArgumentException: object is not an instance of declaring class
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	*__checkpoint ⇢ Handler se.lantmateriet.tacodus.orderhanteraremock.valueclassbugg.FooController#foo(Continuation) [DispatcherHandler]
	*__checkpoint ⇢ HTTP GET "/foo" [ExceptionHandlingWebHandler]
Original Stack Trace:
		at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
		at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
		at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
		at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
		at kotlin.reflect.jvm.internal.calls.ValueClassAwareCaller.call(ValueClassAwareCaller.kt:190) ~[kotlin-reflect-1.9.22.jar:1.9.22-release-704]
		at kotlin.reflect.jvm.internal.KCallableImpl.callDefaultMethod$kotlin_reflection(KCallableImpl.kt:207) ~[kotlin-reflect-1.9.22.jar:1.9.22-release-704]
		at kotlin.reflect.full.KCallables.callSuspendBy(KCallables.kt:74) ~[kotlin-reflect-1.9.22.jar:1.9.22-release-704]
		at org.springframework.core.CoroutinesUtils.lambda$invokeSuspendingFunction$2(CoroutinesUtils.java:124) ~[spring-core-6.1.2.jar:6.1.2]
		at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$4.invokeSuspend(IntrinsicsJvm.kt:270) ~[kotlin-stdlib-1.9.22.jar:1.9.22-release-704]
		at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[kotlin-stdlib-1.9.22.jar:1.9.22-release-704]
		at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:68) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:375) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
		at kotlinx.coroutines.reactor.MonoKt.monoInternal$lambda$2(Mono.kt:92) ~[kotlinx-coroutines-reactor-1.7.3.jar:na]
		at reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:61) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.FluxFromMonoOperator.subscribe(FluxFromMonoOperator.java:85) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.FluxDeferContextual.subscribe(FluxDeferContextual.java:57) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber.onNext(MonoSubscribeOn.java:146) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:200) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:293) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:188) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoZip$ZipCoordinator.signal(MonoZip.java:297) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoZip$ZipInner.onNext(MonoZip.java:478) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2571) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoZip$ZipInner.onSubscribe(MonoZip.java:470) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onSubscribe(MonoPeekTerminal.java:152) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoZip$ZipCoordinator.request(MonoZip.java:220) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onSubscribe(MonoIgnoreThen.java:135) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoZip.subscribe(MonoZip.java:129) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:241) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:204) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.onComplete(MonoFlatMap.java:189) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.Operators.complete(Operators.java:137) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoZip.subscribe(MonoZip.java:121) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:264) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.Mono.subscribe(Mono.java:4512) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber.run(MonoSubscribeOn.java:126) ~[reactor-core-3.6.1.jar:3.6.1]
		at reactor.core.scheduler.ExecutorScheduler$ExecutorTrackedRunnable.run(ExecutorScheduler.java:192) ~[reactor-core-3.6.1.jar:3.6.1]
		at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
		at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
		at java.base/java.lang.Thread.run(Thread.java:840) ~[na:na]

If I change @Repository to @Service it works as expected.

Regards, Peter

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Dec 22, 2023
@sdeleuze
Copy link
Contributor

Can you please test with Spring Framework 6.1.3-SNAPSHOT?

@sdeleuze sdeleuze added the theme: kotlin An issue related to Kotlin support label Dec 22, 2023
@sdeleuze sdeleuze self-assigned this Dec 22, 2023
@sdeleuze sdeleuze added the status: waiting-for-feedback We need additional information before we can continue label Dec 22, 2023
@pwestlin
Copy link
Author

pwestlin commented Dec 22, 2023

Can you please test with Spring Framework 6.1.3-SNAPSHOT?

How do I accomplish that in a manageable way? :)
I've tried with Spring Boot 3.2.2-SNAPSHOT but that includes Spring 6.1.2, just like Spring Boot 3.2.1.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 22, 2023
@pwestlin pwestlin changed the title Unable to use kotlin value classes as function arguments to functions in @Repository when having dependency to org.springframework:spring-tx on the classpath Unable to use Kotlin value classes as function arguments to functions in @Repository when having dependency to org.springframework:spring-tx on the classpath Dec 22, 2023
@quaff
Copy link
Contributor

quaff commented Dec 25, 2023

Can you please test with Spring Framework 6.1.3-SNAPSHOT?

How do I accomplish that in a manageable way? :) I've tried with Spring Boot 3.2.2-SNAPSHOT but that includes Spring 6.1.2, just like Spring Boot 3.2.1.

You can set spring-framework.version=6.1.3-SNAPSHOT in gradle.properties, and make sure snapshot maven repository is included.

	maven {
		url "https://repo.spring.io/snapshot"
	}

@pwestlin
Copy link
Author

pwestlin commented Jan 2, 2024

Can you please test with Spring Framework 6.1.3-SNAPSHOT?

It works with spring-framework.version=6.1.3-SNAPSHOT.

@bclozel
Copy link
Member

bclozel commented Jan 2, 2024

Thanks for letting us know, this means this has been fixed already in #31846
I'll close this issue as a duplicate, then.

@bclozel bclozel closed this as not planned Won't fix, can't repro, duplicate, stale Jan 2, 2024
@bclozel bclozel added status: duplicate A duplicate of another issue and removed status: waiting-for-triage An issue we've not yet triaged or decided on status: feedback-provided Feedback has been provided labels Jan 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: duplicate A duplicate of another issue theme: kotlin An issue related to Kotlin support
Projects
None yet
Development

No branches or pull requests

5 participants