diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization/deployment/src/main/java/io/quarkus/kotlin/serialization/deployment/KotlinSerializationProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization/deployment/src/main/java/io/quarkus/kotlin/serialization/deployment/KotlinSerializationProcessor.java index 192c6e14e51b4..f393916762e33 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization/deployment/src/main/java/io/quarkus/kotlin/serialization/deployment/KotlinSerializationProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization/deployment/src/main/java/io/quarkus/kotlin/serialization/deployment/KotlinSerializationProcessor.java @@ -3,16 +3,23 @@ import static io.quarkus.deployment.Feature.RESTEASY_REACTIVE_KOTLIN_SERIALIZATION; import static io.quarkus.resteasy.reactive.common.deployment.ServerDefaultProducesHandlerBuildItem.json; +import java.util.ArrayList; import java.util.List; import javax.ws.rs.Priorities; import javax.ws.rs.RuntimeType; import javax.ws.rs.core.MediaType; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; +import org.jboss.jandex.DotName; + import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.kotlin.serialization.KotlinSerializationMessageBodyReader; import io.quarkus.kotlin.serialization.KotlinSerializationMessageBodyWriter; import io.quarkus.resteasy.reactive.common.deployment.ServerDefaultProducesHandlerBuildItem; @@ -20,6 +27,31 @@ import io.quarkus.resteasy.reactive.spi.MessageBodyWriterBuildItem; public class KotlinSerializationProcessor { + + private static final DotName SERIALIZABLE = DotName.createSimple("kotlinx.serialization.Serializable"); + private static final String COMPANION_CLASS_SUFFIX = "$Companion"; + + // Kotlin Serialization generates classes at compile time which need to be available via reflection + // for serialization to work properly + @BuildStep + public void registerReflection(CombinedIndexBuildItem index, BuildProducer reflectiveClass) { + var serializableInstances = index.getIndex().getAnnotations(SERIALIZABLE); + if (serializableInstances.isEmpty()) { + return; + } + + List supportClassNames = new ArrayList<>(2 * serializableInstances.size()); + for (AnnotationInstance instance : serializableInstances) { + if (instance.target().kind() != AnnotationTarget.Kind.CLASS) { + continue; + } + var targetClass = instance.target().asClass().name(); + String companionClassName = targetClass.toString() + COMPANION_CLASS_SUFFIX; + supportClassNames.add(companionClassName); + } + reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, false, supportClassNames.toArray(new String[0]))); + } + @BuildStep public void additionalProviders( BuildProducer additionalBean, diff --git a/integration-tests/kotlin-serialization/pom.xml b/integration-tests/kotlin-serialization/pom.xml index e72059383f116..450808021646d 100644 --- a/integration-tests/kotlin-serialization/pom.xml +++ b/integration-tests/kotlin-serialization/pom.xml @@ -119,12 +119,6 @@ - - maven-failsafe-plugin - - true - - io.quarkus quarkus-maven-plugin diff --git a/integration-tests/kotlin-serialization/src/main/kotlin/io/quarkus/it/kotser/model/Person.kt b/integration-tests/kotlin-serialization/src/main/kotlin/io/quarkus/it/kotser/model/Person.kt index 12191e7cafd10..145ca2a6e9658 100644 --- a/integration-tests/kotlin-serialization/src/main/kotlin/io/quarkus/it/kotser/model/Person.kt +++ b/integration-tests/kotlin-serialization/src/main/kotlin/io/quarkus/it/kotser/model/Person.kt @@ -1,10 +1,12 @@ package io.quarkus.it.kotser.model +import io.quarkus.runtime.annotations.RegisterForReflection import kotlinx.serialization.Serializable @Serializable +@RegisterForReflection data class Person(var name: String, var defaulted: String = "hi there!") { override fun toString(): String { TODO("this shouldn't get called. a proper serialization should be invoked.") } -} \ No newline at end of file +} diff --git a/integration-tests/kotlin-serialization/src/test/kotlin/io/quarkus/it/kotser/ResourceIT.kt b/integration-tests/kotlin-serialization/src/test/kotlin/io/quarkus/it/kotser/ResourceIT.kt new file mode 100644 index 0000000000000..5db6ab0d29922 --- /dev/null +++ b/integration-tests/kotlin-serialization/src/test/kotlin/io/quarkus/it/kotser/ResourceIT.kt @@ -0,0 +1,7 @@ +package io.quarkus.it.kotser + +import io.quarkus.test.junit.QuarkusIntegrationTest + +@QuarkusIntegrationTest +class ResourceIT : ResourceTest() { +} diff --git a/integration-tests/kotlin-serialization/src/test/kotlin/io/quarkus/it/kotser/ResourceTest.kt b/integration-tests/kotlin-serialization/src/test/kotlin/io/quarkus/it/kotser/ResourceTest.kt index f7b11b299cf25..aa5d8efa84182 100644 --- a/integration-tests/kotlin-serialization/src/test/kotlin/io/quarkus/it/kotser/ResourceTest.kt +++ b/integration-tests/kotlin-serialization/src/test/kotlin/io/quarkus/it/kotser/ResourceTest.kt @@ -7,7 +7,7 @@ import org.hamcrest.CoreMatchers.`is` import org.junit.jupiter.api.Test @QuarkusTest -class ResourceTest { +open class ResourceTest { @Test fun testGet() { given() @@ -37,4 +37,4 @@ class ResourceTest { "defaulted": "hi there!" }""".trimIndent())) } -} \ No newline at end of file +}