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

Kotlin data class serialization fails with GraalVM >= 22.1 #28167

Closed
krisgerhard opened this issue Sep 23, 2022 · 18 comments · Fixed by #29033
Closed

Kotlin data class serialization fails with GraalVM >= 22.1 #28167

krisgerhard opened this issue Sep 23, 2022 · 18 comments · Fixed by #29033
Labels
Milestone

Comments

@krisgerhard
Copy link

Describe the bug

I'm trying to serialize a Kotlin data class with kotlinx.serialization annotations.
Everything is working as expected with GraalVM 22.0 but starts throwing errors with 22.1 and 22.2.

Data class:

@Serializable
@RegisterForReflection
data class Participant(
  val id: String,
  val name: String,
  val avatar: String,
  val type: String,
)

Expected behavior

No error

Actual behavior

Error during serialization.
Stacktrace:

kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Could not compute caller for function: public final fun serializer(): kotlinx.serialization.KSerializer<com.wrcfan.api.repository.model.Participant> defined in com.wrcfan.api.repository.model.Participant.Companion[DeserializedSimpleFunctionDescriptor@4165686b] (member = null)
	at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:88)
	at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:61)
	at kotlin.reflect.jvm.internal.ReflectProperties$LazyVal.invoke(ReflectProperties.java:63)
	at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
	at kotlin.reflect.jvm.internal.KFunctionImpl.getCaller(KFunctionImpl.kt:61)
	at kotlin.reflect.jvm.internal.KParameterImpl$type$1.invoke(KParameterImpl.kt:56)
	at kotlin.reflect.jvm.internal.KParameterImpl$type$1.invoke(KParameterImpl.kt:43)
	at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:93)
	at kotlin.reflect.jvm.internal.KTypeImpl.getJavaType(KTypeImpl.kt:47)
	at kotlin.reflect.jvm.ReflectJvmMapping.getJavaType(ReflectJvmMapping.kt:81)
	at com.fasterxml.jackson.module.kotlin.KotlinNamesAnnotationIntrospectorKt.isPossibleSingleString(KotlinNamesAnnotationIntrospector.kt:152)
	at com.fasterxml.jackson.module.kotlin.KotlinNamesAnnotationIntrospectorKt.filterOutSingleStringCallables(KotlinNamesAnnotationIntrospector.kt:156)
	at com.fasterxml.jackson.module.kotlin.KotlinNamesAnnotationIntrospectorKt.access$filterOutSingleStringCallables(KotlinNamesAnnotationIntrospector.kt:1)
	at com.fasterxml.jackson.module.kotlin.KotlinNamesAnnotationIntrospector.hasCreatorAnnotation(KotlinNamesAnnotationIntrospector.kt:86)
	at com.fasterxml.jackson.module.kotlin.KotlinNamesAnnotationIntrospector.access$hasCreatorAnnotation(KotlinNamesAnnotationIntrospector.kt:30)
	at com.fasterxml.jackson.module.kotlin.KotlinNamesAnnotationIntrospector$hasCreatorAnnotation$2.invoke(KotlinNamesAnnotationIntrospector.kt:97)
	at com.fasterxml.jackson.module.kotlin.KotlinNamesAnnotationIntrospector$hasCreatorAnnotation$2.invoke(KotlinNamesAnnotationIntrospector.kt:97)
	at com.fasterxml.jackson.module.kotlin.ReflectionCache.checkConstructorIsCreatorAnnotated(ReflectionCache.kt:85)
	at com.fasterxml.jackson.module.kotlin.KotlinNamesAnnotationIntrospector.hasCreatorAnnotation(KotlinNamesAnnotationIntrospector.kt:97)
	at com.fasterxml.jackson.databind.AnnotationIntrospector.findCreatorAnnotation(AnnotationIntrospector.java:1514)
	at com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair.findCreatorAnnotation(AnnotationIntrospectorPair.java:842)
	at com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair.findCreatorAnnotation(AnnotationIntrospectorPair.java:842)
	at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._addCreatorParam(POJOPropertiesCollector.java:651)
	at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._addCreators(POJOPropertiesCollector.java:618)
	at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.collectAll(POJOPropertiesCollector.java:426)
	at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getJsonValueAccessor(POJOPropertiesCollector.java:270)
	at com.fasterxml.jackson.databind.introspect.BasicBeanDescription.findJsonValueAccessor(BasicBeanDescription.java:258)
	at com.fasterxml.jackson.databind.ser.BasicSerializerFactory.findSerializerByAnnotations(BasicSerializerFactory.java:391)
	at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._createSerializer2(BeanSerializerFactory.java:224)
	at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:173)
	at com.fasterxml.jackson.databind.SerializerProvider._createUntypedSerializer(SerializerProvider.java:1495)
	at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1463)
	at com.fasterxml.jackson.databind.SerializerProvider.findValueSerializer(SerializerProvider.java:585)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.resolve(BeanSerializerBase.java:376)
	at com.fasterxml.jackson.databind.ser.SerializerCache.addAndResolveNonTypedSerializer(SerializerCache.java:174)
	at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1473)
	at com.fasterxml.jackson.databind.SerializerProvider.findContentValueSerializer(SerializerProvider.java:747)
	at com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase.createContextual(AsArraySerializerBase.java:208)
	at com.fasterxml.jackson.databind.SerializerProvider.handlePrimaryContextualization(SerializerProvider.java:1085)
	at com.fasterxml.jackson.databind.SerializerProvider.findPrimaryPropertySerializer(SerializerProvider.java:686)
	at com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap.findAndAddPrimarySerializer(PropertySerializerMap.java:72)
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter._findAndAddDynamic(BeanPropertyWriter.java:895)
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:706)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774)
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774)
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
	at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContentsUsing(CollectionSerializer.java:171)
	at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:116)
	at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:107)
	at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:25)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:400)
	at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1514)
	at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1007)
	at org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider.writeTo(ResteasyJackson2Provider.java:345)
	at io.quarkus.resteasy.common.runtime.jackson.QuarkusJacksonSerializer.writeTo(QuarkusJacksonSerializer.java:70)
	at org.jboss.resteasy.core.messagebody.AsyncBufferedMessageBodyWriter.asyncWriteTo(AsyncBufferedMessageBodyWriter.java:24)
	at io.quarkus.resteasy.common.runtime.jackson.QuarkusJacksonSerializer.asyncWriteTo(QuarkusJacksonSerializer.java:77)
	at org.jboss.resteasy.core.interception.jaxrs.ServerWriterInterceptorContext.writeTo(ServerWriterInterceptorContext.java:87)
	at org.jboss.resteasy.core.interception.jaxrs.AbstractWriterInterceptorContext.asyncProceed(AbstractWriterInterceptorContext.java:203)
	at org.jboss.resteasy.core.interception.jaxrs.AbstractWriterInterceptorContext.getStarted(AbstractWriterInterceptorContext.java:166)
	at org.jboss.resteasy.core.interception.jaxrs.ServerWriterInterceptorContext.lambda$getStarted$0(ServerWriterInterceptorContext.java:73)
	at org.jboss.resteasy.core.interception.jaxrs.ServerWriterInterceptorContext.aroundWriteTo(ServerWriterInterceptorContext.java:93)
	at org.jboss.resteasy.core.interception.jaxrs.ServerWriterInterceptorContext.getStarted(ServerWriterInterceptorContext.java:73)
	at org.jboss.resteasy.core.ServerResponseWriter.lambda$writeNomapResponse$3(ServerResponseWriter.java:163)
	at org.jboss.resteasy.core.interception.jaxrs.ContainerResponseContextImpl.filter(ContainerResponseContextImpl.java:410)
	at org.jboss.resteasy.core.ServerResponseWriter.executeFilters(ServerResponseWriter.java:252)
	at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:101)
	at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:74)
	at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:594)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:524)
	at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
	at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
	at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
	at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
	at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
	at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151)
	at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:91)
	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:560)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:833)
	at com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:705)
	at com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:202)

How to Reproduce?

No response

Output of uname -a or ver

No response

Output of java -version

17.0.4+8-jvmci-22.2-b06

GraalVM version (if different from Java)

GraalVM 22.2.0 Java 17 CE

Quarkus version or git rev

2.13.0.Final

Build tool (ie. output of mvnw --version or gradlew --version)

mvn

Additional information

No response

@krisgerhard krisgerhard added the kind/bug Something isn't working label Sep 23, 2022
@quarkus-bot
Copy link

quarkus-bot bot commented Sep 23, 2022

/cc @evanchooly, @geoand

@geoand
Copy link
Contributor

geoand commented Sep 23, 2022

Adding @zakkak and @jerboaa who might know what's going on the GraalVM side

@zakkak
Copy link
Contributor

zakkak commented Sep 23, 2022

Unfortunately I can't help much. My understanding is that kotlin tries to reflectively access the caller of the method and fails because reflection is not properly configured.

Looking at the error I suspect that you need to register com.wrcfan.api.repository.model.Participant.Companion for reflection as well.

You might also want to run your application with the native-image-agent and have a look at the reflection configuration produced by it. This might give you more hints on which classes you need to register for reflection.

@krisgerhard
Copy link
Author

Indeed error disappears if I add reflection config for the Companion object.
But doing it for every model is not really a solution.

@geoand
Copy link
Contributor

geoand commented Sep 23, 2022

What does the entire Participant code look like?

@krisgerhard
Copy link
Author

package com.wrcfan.api.repository.model

import io.quarkus.runtime.annotations.RegisterForReflection
import kotlinx.serialization.Serializable

@Serializable
@RegisterForReflection
data class Participant(
  val id: String,
  val name: String,
  val avatar: String,
  val type: String,
)

@geoand
Copy link
Contributor

geoand commented Sep 23, 2022

I guess one solution here would be to also automatically register the companion object for data classes.

@geoand
Copy link
Contributor

geoand commented Sep 23, 2022

Can you try doing:

@Serializable
@RegisterForReflection(ignoreNested=false)
data class Participant(
  val id: String,
  val name: String,
  val avatar: String,
  val type: String,
)

please?

@krisgerhard
Copy link
Author

Same result :(

@geoand
Copy link
Contributor

geoand commented Sep 23, 2022

Okay, thanks

@krisgerhard
Copy link
Author

krisgerhard commented Sep 23, 2022

Maybe it is somehow related to Kotlin/kotlinx.serialization#1348 but it really goes beyond my understanding of the compilation process :)

More interesting to me is why it is working with some GraalVM versions.

@geoand
Copy link
Contributor

geoand commented Sep 23, 2022

In this case you are using Jackson, not Kotlin Serialization

@krisgerhard
Copy link
Author

True, but I'm using the compiler plugin which alters the compiled model object

@geoand
Copy link
Contributor

geoand commented Sep 23, 2022

I am pretty sure that if you remove @kotlinx.serialization.Serializable, the result will be the same :)

@krisgerhard
Copy link
Author

krisgerhard commented Nov 3, 2022

Can you try doing:

@Serializable
@RegisterForReflection(ignoreNested=false)
data class Participant(
  val id: String,
  val name: String,
  val avatar: String,
  val type: String,
)

please?

Using newer mandrel image with @RegisterForReflection(ignoreNested=false) resolves the issue.
quarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel:22.3.0.1-Final-java17

I guess one solution here would be to also automatically register the companion object for data classes.

How could we do this automatically?

@geoand
Copy link
Contributor

geoand commented Nov 3, 2022

Using newer mandrel image with @RegisterForReflection(ignoreNested=false) resolves the issue.
quarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel:22.3.0.1-Final-java17

Thanks for the update!

How could we do this automatically?

It would require a change in Quarkus

@krisgerhard
Copy link
Author

Also, fyi. I was testing with Quarkus 2.14.0.Final

geoand added a commit to geoand/quarkus that referenced this issue Nov 3, 2022
Users have come to expect that @RegisterForReflection fixes
a lot of native image issues and use it a lot.
For Kotlin classes however, registering a class is often not
enough - the companion classes are need to be registered.
These companion classes are nested classes, so we automatically
set the property to true for Kotlin classes

Closes: quarkusio#28167
@geoand
Copy link
Contributor

geoand commented Nov 3, 2022

#29033 should do the trick for automatically setting ignoreNested to false for Kotlin classes.

gsmet added a commit that referenced this issue Nov 4, 2022
Automatically register nested Kotlin classes for reflection
@quarkus-bot quarkus-bot bot added this to the 2.15 - main milestone Nov 4, 2022
@gsmet gsmet modified the milestones: 2.15 - main, 2.14.1.Final Nov 5, 2022
gsmet pushed a commit to gsmet/quarkus that referenced this issue Nov 5, 2022
Users have come to expect that @RegisterForReflection fixes
a lot of native image issues and use it a lot.
For Kotlin classes however, registering a class is often not
enough - the companion classes are need to be registered.
These companion classes are nested classes, so we automatically
set the property to true for Kotlin classes

Closes: quarkusio#28167
(cherry picked from commit 6274863)
iocanel pushed a commit to iocanel/quarkus that referenced this issue Nov 7, 2022
Users have come to expect that @RegisterForReflection fixes
a lot of native image issues and use it a lot.
For Kotlin classes however, registering a class is often not
enough - the companion classes are need to be registered.
These companion classes are nested classes, so we automatically
set the property to true for Kotlin classes

Closes: quarkusio#28167
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants