Describe the bug
When using the Jackson Kotlin module with a Kotlin data class, it fails in native mode with the following:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.capitalone.polyglot.quarkus.model.Address` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
The example code is pretty straight forward:
@ApplicationScoped
class CustomObjectMapperConfig {
@Singleton
@Produces
fun objectMapper(): ObjectMapper {
return ObjectMapper().registerModule(KotlinModule())
}
}
data class Address(val addressLine1 : String, val addressLine2 : String, val addressLine3 : String, val addressLine4 : String, val city : String, val state : String, val country : String)
@Path("/deposits/tooling/validate-address")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
class AddressValidationResource {
@POST
fun validateAddress(address : Address) = address.copy(
addressLine1 = "${address.addressLine1} ${address.addressLine2} ${address.addressLine3} ${address.addressLine4}",
addressLine2 = "",
addressLine3 = "",
addressLine4 = "")
}
Expected behavior
(Describe the expected behavior clearly and concisely.)
If I run this in JVM mode, aka mvn compile quarkus:dev
, or as a runnable jar it works just fine.
Actual behavior
(Describe the actual behavior clearly and concisely.)
To Reproduce
Steps to reproduce the behavior:
Configuration
# Add your application.properties here, if applicable.
Screenshots
(If applicable, add screenshots to help explain your problem.)
Environment (please complete the following information):
uname -a
or ver
: java -version
: openjdk version "1.8.0_212"
OpenJDK Runtime Environment (build 1.8.0_212-20190523183630.graal2.jdk8u-src-tar-gz-b03)
OpenJDK 64-Bit GraalVM CE 19.1.0 (build 25.212-b03-jvmci-20-b04, mixed mode)
<quarkus.version>0.21.2</quarkus.version>
Additional context
(Add any other context about the problem here.)
Related #3652
For a short-term work-around the following working:
@RegisterForReflection
data class Address(var addressLine1 : String = "",
var addressLine2 : String = "",
var addressLine3 : String = "",
var addressLine4 : String = "",
var city : String = "",
var state : String = "",
var country : String = "")
I think this is a legit issue. As a parameter of a REST method, the Address
type should be registered for reflection automatically.
There's something wrong going on.
@geoand any chance you could have a look to this one?
@gsmet sure thing
This doesn't appear to be a Quarkus issue. The Address
class does get registered for reflection correctly when I tested.
In order to confirm this, @rdifrango can you please use your workaround without the @RegisterForReflection
annotation? If I am correct, it should still work.
In that case you'll need to go read up on what the proper way to use Kotlin data classes and Jackson.
@geoand you're correct, it still works if I remove @RegisterForReflection
.
I also tested by swap back to val
from var
but with default values and that's when it fails again, so working:
data class Address(var addressLine1 : String = "",
var addressLine2 : String = "",
var addressLine3 : String = "",
var addressLine4 : String = "",
var city : String = "",
var state : String = "",
var country : String = "")
NOT WORKING
data class Address(val addressLine1 : String = "",
val addressLine2 : String = "",
val addressLine3 : String = "",
val addressLine4 : String = "",
val city : String = "",
val state : String = "",
val country : String = "")
Thanks for confirming and looking into it.
It certainly looks like it's an issue with how Kotlin and Jackson interact.
So I will go ahead and close this since it doesn't appear to be a Quarkus issue. If you feel that is not correct, please reopen and provide an update.
Thanks
@geoand I still think this is an issue as the NOT WORKING case, works just fine in JVM mode, it only fails in NATIVE mode.
@rdifrango a small reproducer then where I can see the exact error you are getting?
The code above is exactly what I used, nothing more nothing less :)
To build it we issue:
mvn package -Pnative -Dnative-image.docker-build=true
To run it:
docker run --rm -p 9090:8080 address-validation-quarkus-kotlin:1.0
If you could upload it to github or something, so I don't make any mistakes while trying to reproduce that would be great :)
@geoand , I'll see what I can do, sadly uploading to GH is non-trivial at the office :)
Here's the maven POM file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.polyglot</groupId>
<artifactId>address-validation-quarkus-kotlin</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<kotlin.version>1.3.21</kotlin.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.version>0.21.2</quarkus.version>
<sortpom.plugin.version>2.8.0</sortpom.plugin.version>
<surefire-plugin.version>2.22.0</surefire-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bom</artifactId>
<version>${quarkus.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kotlin</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>com.github.ekryd.sortpom</groupId>
<artifactId>sortpom-maven-plugin</artifactId>
<version>${sortpom.plugin.version}</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sort</goal>
</goals>
</execution>
</executions>
<configuration>
<predefinedSortOrder>default_1_0_0</predefinedSortOrder>
<lineSeparator>\n</lineSeparator>
<encoding>${project.build.sourceEncoding}</encoding>
<sortProperties>true</sortProperties>
<keepBlankLines>true</keepBlankLines>
<createBackupFile>false</createBackupFile>
<sortDependencies>scope</sortDependencies>
<nrOfIndentSpace>4</nrOfIndentSpace>
</configuration>
</plugin>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.version}</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemProperties>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
</systemProperties>
</configuration>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
<configuration>
<compilerPlugins>
<plugin>all-open</plugin>
</compilerPlugins>
<pluginOptions>
<option>all-open:annotation=javax.ws.rs.Path</option>
<option>all-open:annotation=javax.enterprise.context.ApplicationScoped</option>
</pluginOptions>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.version}</version>
<executions>
<execution>
<goals>
<goal>native-image</goal>
</goals>
<configuration>
<enableHttpUrlHandler>true</enableHttpUrlHandler>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemProperties>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
</systemProperties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Thanks @rdifrango, I'll try and reproduce it. One last thing, do you have a curl command for POSTing to the endpoint?
curl -X POST -H "content-type:application/json" -H "accept:application/json" http://localhost:9090/deposits/tooling/validate-address -d '{"addressLine1":"asdfasdf line 45", "addressLine2":"line2","addressLine3":"line3","addressLine4":"line4","city":"phl","state":"pa","country":"usa"}'
Thanks, I'll try it and let you know
I was able to reproduce the problem.
It doesn't look like a regular Quarkus problem (since the class is registered for reflection), but more some kind of weird interop issue.
I am guessing it will take a lot more digging than I can do at the moment, so let's leave it open and I'll take a look when I have som extra time.
thank you @geoand!
Actually what we are seeing seems consistent with the Kotlin module not being applied in native mode.
So if anyone wants to explore more, that would be great. I will of course eventually get to this but frankly since we have workable workarounds and the root cause it probably hidden deep, I wouldn't consider it very high priority :)
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you!
We are doing this automatically to ensure out-of-date issues does not stay around indefinitely.
If you believe this issue is still relevant please put a comment on it on why and if it truly needs to stay request or add 'pinned' label.
Would be nice to have this fixed.
I have faced the same problem on Java (JDK 8). The problem just appears running natively.
The @RegisterForReflection
workaround as @rdifrango mentioned did the trick.
If someone want to check the code, this is the repository.
@odilonjk the problem you are seeing is different. In your case it is not a bug but because you are returning Response
, it is necessary to use @RegisterForReflection
, see: https://quarkus.io/guides/rest-json#using-response
Is there something specific needed to do to solve this issue, other than upgrading to 1.1.1?
I am still getting the same issue when running in native mode com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of
xxxx(no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
This works fine in JVM mode, but not native. I tried with RegisterForReflection, but this did not work, and also, I am not using Response, so this should not be needed.
For reference, my source code is here --> https://github.com/codemwnci/tododo/blob/master/todo-server/src/main/kotlin/codemwnci/TodoResource.kt
@codemwnci is your To-do class is a different module perhaps?
Not that I am aware of. All the Kotlin code is contained within that one KT file.
Okay, I'll have to give it a look over the few days
On Thu, Jan 9, 2020, 15:53 Wayne Ellis notifications@github.com wrote:
Not that I am aware of. All the Kotlin code is contained within that one
KT file.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/quarkusio/quarkus/issues/3954?email_source=notifications&email_token=ABBMDP4DQZS3NWD27BLLDADQ4425BA5CNFSM4IVMG2G2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEIQSAVY#issuecomment-572596311,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/ABBMDPZM3IVOM2DHHQGIVNDQ4425BANCNFSM4IVMG2GQ
.
There might be another issue that we haven't taken into account yet
On Thu, Jan 9, 2020, 15:54 Georgios Andrianakis geoand@gmail.com wrote:
Okay, I'll have to give it a look over the few days
On Thu, Jan 9, 2020, 15:53 Wayne Ellis notifications@github.com wrote:
Not that I am aware of. All the Kotlin code is contained within that one
KT file.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/quarkusio/quarkus/issues/3954?email_source=notifications&email_token=ABBMDP4DQZS3NWD27BLLDADQ4425BA5CNFSM4IVMG2G2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEIQSAVY#issuecomment-572596311,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/ABBMDPZM3IVOM2DHHQGIVNDQ4425BANCNFSM4IVMG2GQ
.
Do we maybe need Jackson 2.10.x ?
(The bom enforces 2.9.x)
Just to confirm, the workaround (var replacing val, and default values) works.
I will test upgrading Jackson from 2.9.7 to 2.10.1 and feedback shortly.
The Jackson version shouldn't be an issue, but I need to check
I see a couple issues in your reproducer:
1.1.1.Final
Todo
needs is not a Kotlin data-class
. I hadn't pushed the latest version of the POM to Github, but I am using the 1.1.1.Final locally. I'll change class
, to data class
and see if it works.
I can also confirm that using 2.10.1 of Jackson doesn't make a difference.
OK, let us know how it goes please
Same error with
class Todo(val id: Long, val txt: String, val completed: Boolean = false)
or
data class Todo(val id: Long, val txt: String, val completed: Boolean = false)
I'll try a clean project (just drop in the config and TodoResource.kt) as a final test, but I am not expecting this to make any difference.
Just for completeness, I am also running the build and run inside of docker, but again, I don't expect that to make any difference.
I'll reopen this since I was able to reproduce it as well, but I think there is probably something else and that the original issue has been fixed.
Pinging myself (@geoand) just so I can get this to the top of my email list 😎
The only way I see me being able to debug this is building Jackson locally with a bunch of println
statements since there is no easy way to debug in native and the stacktrace doesn't provide any useful info...
The issue seems to occur only when a (data) class has val
properties some of which have default values and some of which do not.
In that case for native to work, all values need to specified in the json object otherwise it fails
The issue comes down to the fact that the kotlin.Metadata
annotation that the Kotlin compiler adds to the class (and which is used here to determine whether a class is a Kotlin class) is not present on the class in the native image.
This is of course begs the question of why the annotation is dropped which I'll try and figure out and update the issue.
So based on a suggestion from @gsmet when registering kotlin.Metadata
from reflection, the process gets further but still fails. The latest error is:
Caused by: java.lang.AssertionError: Built-in class kotlin.Any is not found
at kotlin.reflect.jvm.internal.impl.builtins.KotlinBuiltIns$3.invoke(KotlinBuiltIns.java:113)
at kotlin.reflect.jvm.internal.impl.builtins.KotlinBuiltIns$3.invoke(KotlinBuiltIns.java:108)
at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$MapBasedMemoizedFunction.invoke(LockBasedStorageManager.java:446)
at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$MapBasedMemoizedFunctionToNotNull.invoke(LockBasedStorageManager.java:521)
at kotlin.reflect.jvm.internal.impl.builtins.KotlinBuiltIns.getBuiltInClassByName(KotlinBuiltIns.java:362)
at kotlin.reflect.jvm.internal.impl.builtins.KotlinBuiltIns.getAny(KotlinBuiltIns.java:367)
at kotlin.reflect.jvm.internal.impl.builtins.KotlinBuiltIns.getAnyType(KotlinBuiltIns.java:642)
at kotlin.reflect.jvm.internal.impl.descriptors.NotFoundClasses$MockClassDescriptor.<init>(NotFoundClasses.kt:60)
at kotlin.reflect.jvm.internal.impl.descriptors.NotFoundClasses$classes$1.invoke(NotFoundClasses.kt:44)
at kotlin.reflect.jvm.internal.impl.descriptors.NotFoundClasses$classes$1.invoke(NotFoundClasses.kt:22)
at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$MapBasedMemoizedFunction.invoke(LockBasedStorageManager.java:446)
at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$MapBasedMemoizedFunctionToNotNull.invoke(LockBasedStorageManager.java:521)
at kotlin.reflect.jvm.internal.impl.descriptors.NotFoundClasses.getClass(NotFoundClasses.kt:92)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.TypeDeserializer$typeConstructor$1.invoke(TypeDeserializer.kt:109)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.TypeDeserializer.typeConstructor(TypeDeserializer.kt:113)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.TypeDeserializer.simpleType(TypeDeserializer.kt:75)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.TypeDeserializer.type(TypeDeserializer.kt:63)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.MemberDeserializer.valueParameters(MemberDeserializer.kt:417)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.MemberDeserializer.loadConstructor(MemberDeserializer.kt:342)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor.computePrimaryConstructor(DeserializedClassDescriptor.kt:122)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor.access$computePrimaryConstructor(DeserializedClassDescriptor.kt:34)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$primaryConstructor$1.invoke(DeserializedClassDescriptor.kt:65)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$primaryConstructor$1.invoke(DeserializedClassDescriptor.kt:34)
at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:352)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor.getUnsubstitutedPrimaryConstructor(DeserializedClassDescriptor.kt:126)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor.computeConstructors(DeserializedClassDescriptor.kt:129)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor.access$computeConstructors(DeserializedClassDescriptor.kt:34)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$constructors$1.invoke(DeserializedClassDescriptor.kt:66)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$constructors$1.invoke(DeserializedClassDescriptor.kt:34)
at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:352)
at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:408)
at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor.getConstructors(DeserializedClassDescriptor.kt:137)
at kotlin.reflect.jvm.internal.KClassImpl.getConstructorDescriptors(KClassImpl.kt:200)
at kotlin.reflect.jvm.internal.KClassImpl$Data$constructors$2.invoke(KClassImpl.kt:91)
at kotlin.reflect.jvm.internal.KClassImpl$Data$constructors$2.invoke(KClassImpl.kt:44)
at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:92)
at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:31)
at kotlin.reflect.jvm.internal.KClassImpl$Data.getConstructors(KClassImpl.kt)
at kotlin.reflect.jvm.internal.KClassImpl.getConstructors(KClassImpl.kt:235)
at kotlin.reflect.jvm.ReflectJvmMapping.getKotlinFunction(ReflectJvmMapping.kt:144)
at com.fasterxml.jackson.module.kotlin.ReflectionCache.kotlinFromJava(ReflectionCache.kt:44)
at com.fasterxml.jackson.module.kotlin.KotlinNamesAnnotationIntrospector$hasCreatorAnnotation$1.invoke(KotlinNamesAnnotationIntrospector.kt:54)
at com.fasterxml.jackson.module.kotlin.KotlinNamesAnnotationIntrospector$hasCreatorAnnotation$1.invoke(KotlinNamesAnnotationIntrospector.kt:20)
at com.fasterxml.jackson.module.kotlin.ReflectionCache.checkConstructorIsCreatorAnnotated(ReflectionCache.kt:46)
at com.fasterxml.jackson.module.kotlin.KotlinNamesAnnotationIntrospector.hasCreatorAnnotation(KotlinNamesAnnotationIntrospector.kt:52)
at com.fasterxml.jackson.databind.AnnotationIntrospector.findCreatorAnnotation(AnnotationIntrospector.java:1261)
I got to this point as well and couldn't figure out a way to register kotlin.Any, when i tried using a reflect-config.json the native image builder says it's not on the classpath, but the stdlib jar is there. I kinda guessed this is because of some magic where the kotlin.Any builtin isn't a real class and somehow maps to java.lang.Object?
@tellisnz-shift I also tried registering Kotlin.Any
without success. I am knowledgeable of the Kotlin internals so I don't have a path forward for the time being.
I'll try and contact the Kotlin folks and see if I get any info
FYI, I have run into this with Kotlin and Native-Image before using Quarkus. I fixed this by using the Kotlin no-arg compiler plugin. Then you can use a data class with val, and not needing to set a default value, as it will create a no arg constructor accessible by reflections.
Ah, the no-arg
plugin! I completely forgot about that, I'll give it spin soon.
Thanks for the suggestion @Mason-Harding
Seems like I can get somewhere with the no-arg plugin but it still doesn't work properly - the default value isn't used (when a default value is set)
I'm sure we are getting off topic, and a Quarkus dev will yell at us soon, but defaults won't work as they only work when no value is specified, and via that constructor. Jackson will set them to null.
Nobody is going to yell at anyone, we are as super friendly community :)
The problem is that the behavior is different between JVM and native mode. With the no-arg plugin, in JVM the defaults are used which unfortunately is not the case in native mode.
I am running into this same issue, except I get the error
@NotNull method kotlin/reflect/jvm/internal/impl/builtins/KotlinBuiltIns.getBuiltInClassByFqName must not return null
I tried adding kotlin.Metadata to the reflection-config file, and now am also getting
java.lang.AssertionError: Built-in class kotlin.Any is not found
I tried debugging by doing Class.forName(Any::class.java.name)
, which I was able to do when I added java.lang.Object to the reflection-config file, but I still get Built-in class kotlin.Any is not found
when jackson tries to read a value.
Yeah this seems to require a chat with the Kotlin folks to see how to proceed. Unfortunately I haven't had the time to do that :(
hi @geoand - have you been able to get in touch with the kotlin/graalvm folks?
@tellisnz-shift unfortunately no. With all the stuff that needed to be done for 1.3, I didn't follow up after not being able to reach them the first time.
I'll try again this week I hope.
Just as an indicator (I haven't isolated this into a barebones example), in case it helps, I am running into this problem for a data class which does not have any default values (but all properties are val
, and obviously I have registered the Kotlin Jackson module on the mapper). However I do have several enum parameters for which there are default enum values (@JsonEnumDefaultValue
) and I have activated the READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE
on the mapper.
Any news?
I was able to get around this problem by explicitly adding Jackson annotations.
So, this code works in JVM mode but doesn't in native mode
@RegisterForReflection
data class CreateTaskRequest(val name: String, val description: String?)
but this code works in both JVM and native modes and keeps the class immutable.
@RegisterForReflection
data class CreateTaskRequest @JsonCreator constructor(
@JsonProperty("name")
val name: String,
@JsonProperty("description")
val description: String?
)
However, it pollutes my class and I expect to write code without any Jackson annotations as in the first example.
Kotlin: 1.3.71
@miron4dev is that class part of a jax-rs method (I assume it's the payload of a POST request)?
@geoand yes, it's the payload of a POST request.
I want to give something a try soon. I'll come back when I have something to report.
I went through various scenaria to try and reproduce the problems with Kotlin Data classes and Jackson deserialization in native but I failed to uncover any...
Here is where I recorded what I did: https://github.com/geoand/kotlin-jackson-demo
If folks have concrete reproducible examples of issues I would love to know about them.
I need to do a little more testing.
I wondered if it was because you had extracted the data classes out into their own files, rather than inside of the GreetingResource.kt file itself, so I tested with my previous 1.1.1.Final code. This still failed.
I need to upgrade Graal to test with 1.3.2, so this will take me a little longer. Will report back when I can.
@codemwnci Thanks. Feel free to ping me when you have results :)
I have tried a number of different configurations (slow testing cycle due to the 15minute native compile!). Upgrading to Quarkus 1.3.2, and GraalVM 20.x, did not initially make any difference. I then changed the POM to reflect your POM and it worked.
I then individually started removing the POM configurations back to my original and the key (single) element that has made this work is in the Kotlin maven plugin configuration.
<javaParameters>true</javaParameters>
So, as long as the latest POM creation through the maven archetypes generate the above setting (which appears to be the case), then this bug can be closed.
For anyone who is not creating a new project, they simply need to add the parameter to the "configuration" of the kotlin-maven-plugin, as linked to above.
Thanks a lot for the thorough check @codemwnci!
Indeed the project generation now includes <javaParameters>true</javaParameters>
: https://github.com/quarkusio/quarkus/blob/1.4.0.CR1/devtools/platform-descriptor-json/src/main/resources/templates/basic-rest/kotlin/pom.xml-template.ftl#L109 (seems like I added it in #6310)
I'll close this issue as per both our findings
If anyone is having troubles with Kotlin in native mode, check the configuration resources linked here: https://github.com/oracle/graal/issues/2306
Most helpful comment
Nobody is going to yell at anyone, we are as super friendly community :)
The problem is that the behavior is different between JVM and native mode. With the no-arg plugin, in JVM the defaults are used which unfortunately is not the case in native mode.