Kotlin method references (e.g. this::someMethod) have a s nice shorthand for retrieving the reflection java.lang.reflect.Method via the javaMethod property as in this::someMethod.javaMethod.
When using a JAX-RS UriInfo to build location headers, using the reflected method reference is a great way to keep things DRY so as not to repeat path information stated in a @Path annotation.
For example:
@POST
fun create(@Context uriInfo: UriInfo) =
Response
.created(
uriInfo.requestUriBuilder
.path(this::fetch.javaMethod) // Fetches path from the @Path annotation of the "fetch" method
.buildFromMap(mapOf(
"id" to "12345"
))
)
.entity(mapOf("id" to "12345", "name" to "test"))
.build()
@GET
@Path("{id}")
fun fetch(@PathParam("id") id: String) =
Response
.ok()
.entity(mapOf("id" to "12345", "name" to "test"))
.build()
Using Kotlin references is a _great_ shorthand and keeps things type safe and compile type checked as compared to using Java...
In Kotlin
path(this::fetch.javaMethod)
In Java
path(GreetingResource::class.java.getDeclaredMethod("fetch", String::class.java))
As usual, all is well in JVM mode (and both examples work correctly) but in native mode the Kotlin version throws an error deep in the bowels of its reflection library.
Expected behavior
Kotlin's reflection tools work the same as equivalent Java reflection in both JVM and native builds.
Actual behavior
In native builds Kotlin reflection for method references fails.
To Reproduce
Environment (please complete the following information):
uname -a or ver: Darwin #### 19.4.0 Darwin Kernel Version 19.4.0: Wed Mar 4 22:28:40 PST 2020; root:xnu-6153.101.6~15/RELEASE_X86_64 x86_64`
Output of java -version:
openjdk version "14" 2020-03-17
OpenJDK Runtime Environment (build 14+36-1461)
OpenJDK 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)
Quarkus version or git rev:
1.3.2
Build tool (ie. output of mvnw --version or gradlew --version):
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Default locale: en_US, platform encoding: UTF-8
OS name: "mac os x", version: "10.15.4", arch: "x86_64", family: "Mac"
Can you paste the error you get please?
The error is related to invalid state deep inside Kotlin's library. Here is the stack trace...
java.lang.IllegalStateException: @NotNull method kotlin/reflect/jvm/internal/impl/builtins/KotlinBuiltIns.getBuiltInClassByFqName must not return null
at kotlin.reflect.jvm.internal.impl.builtins.KotlinBuiltIns.$$$reportNull$$$0(KotlinBuiltIns.java)
at kotlin.reflect.jvm.internal.impl.builtins.KotlinBuiltIns.getBuiltInClassByFqName(KotlinBuiltIns.java:357)
at kotlin.reflect.jvm.internal.impl.builtins.jvm.JavaToKotlinClassMap.mapJavaToKotlin(JavaToKotlinClassMap.kt:130)
at kotlin.reflect.jvm.internal.impl.builtins.jvm.JavaToKotlinClassMap.mapJavaToKotlin$default(JavaToKotlinClassMap.kt:126)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.types.JavaTypeResolver.mapKotlinClass(JavaTypeResolver.kt:161)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.types.JavaTypeResolver.computeTypeConstructor(JavaTypeResolver.kt:135)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.types.JavaTypeResolver.computeSimpleJavaClassifierType(JavaTypeResolver.kt:117)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.types.JavaTypeResolver.transformJavaClassifierType(JavaTypeResolver.kt:93)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.types.JavaTypeResolver.transformJavaType(JavaTypeResolver.kt:52)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassDescriptor$LazyJavaClassTypeConstructor.computeSupertypes(LazyJavaClassDescriptor.kt:191)
at kotlin.reflect.jvm.internal.impl.types.AbstractTypeConstructor$supertypes$1.invoke(AbstractTypeConstructor.kt:80)
at kotlin.reflect.jvm.internal.impl.types.AbstractTypeConstructor$supertypes$1.invoke(AbstractTypeConstructor.kt:26)
at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:355)
at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValueWithPostCompute.invoke(LockBasedStorageManager.java:428)
at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedNotNullLazyValueWithPostCompute.invoke(LockBasedStorageManager.java:459)
at kotlin.reflect.jvm.internal.impl.types.AbstractTypeConstructor.getSupertypes(AbstractTypeConstructor.kt:27)
at kotlin.reflect.jvm.internal.impl.types.AbstractTypeConstructor.getSupertypes(AbstractTypeConstructor.kt:26)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassMemberScope.computeFunctionNames(LazyJavaClassMemberScope.kt:78)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassMemberScope.computeFunctionNames(LazyJavaClassMemberScope.kt:67)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope$functionNamesLazy$2.invoke(LazyJavaScope.kt:253)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope$functionNamesLazy$2.invoke(LazyJavaScope.kt:59)
at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:355)
at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:474)
at kotlin.reflect.jvm.internal.impl.storage.StorageKt.getValue(storage.kt:42)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope.getFunctionNamesLazy(LazyJavaScope.kt)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope.getFunctionNames(LazyJavaScope.kt:257)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope.getContributedFunctions(LazyJavaScope.kt:266)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassMemberScope.getContributedFunctions(LazyJavaClassMemberScope.kt:747)
at kotlin.reflect.jvm.internal.KClassImpl.getFunctions(KClassImpl.kt:208)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.findFunctionDescriptor(KDeclarationContainerImpl.kt:151)
at kotlin.reflect.jvm.internal.KFunctionImpl$descriptor$2.invoke(KFunctionImpl.kt:56)
at kotlin.reflect.jvm.internal.KFunctionImpl$descriptor$2.invoke(KFunctionImpl.kt:36)
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.KFunctionImpl.getDescriptor(KFunctionImpl.kt)
at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:62)
at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:36)
at kotlin.reflect.jvm.internal.ReflectProperties$LazyVal.invoke(ReflectProperties.java:62)
at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:31)
at kotlin.reflect.jvm.internal.KFunctionImpl.getCaller(KFunctionImpl.kt)
at kotlin.reflect.jvm.ReflectJvmMapping.getJavaMethod(ReflectJvmMapping.kt:62)
@geoand I just hit this. It's a known issue https://github.com/oracle/graal/issues/2306. The workaround only partially worked for me, requiring tweak to resources.json. Probably needs a fix in quarkus-kotlin or a new quarkus-kotlin-reflect extension.
https://github.com/oracle/graal/issues/2306
{
"resources": [
{
"pattern": "META-INF/.*.kotlin_module$"
},
{
"pattern": "META-INF/services/kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoader"
},
{
"pattern": "META-INF/services/kotlin.reflect.jvm.internal.impl.resolve.ExternalOverridabilityCondition"
},
{
"pattern": ".*.kotlin_builtins"
}
]
}
With the resources.json in place, Jackson started breaking with:
Failed to run lambda: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Class com.acme.MyClass[] is instantiated reflectively but was never registered. Register the class by using org.graalvm.nativeimage.hosted.RuntimeReflection
I have never seen the com.acme.MyClass[] syntax before. Apparently it is not legal in Kotlin? What is this even called in Java? Either way, adding a reflection-config.json to my build worked (in addition to annotating the class with @RegisterForReflection. )
[
{
"name": "com.acme.MyClass[]",
"allPublicMethods": true,
"allDeclaredFields":true,
"allDeclaredMethods":true,
"allDeclaredConstructors":true
}
]
Anyhow, with all these tweaks it works. Quarkus 1.5.2 and graal 20.1.0-java8
Thanks for the information @sherl0cks.
I think we can probably make the current quarkus-kotlin extension register the info automatically, shouldn't be hard.
@RotBolt would you perhaps like to take a look?
Sounds good @geoand.
I'd really love some more info on the MyClass[] syntax. What is the name of this syntax? Why does Graal need it? Why does it not work with kotlin and @RegisterForReflection? One of the strangest things I've seen in my graal/quarkus journey...
I think it's just the array syntax written in a more readable form.
Can you try substituting MyClass[] with [LMyClass?
@geoand will look into this 馃憤
This seems to be the info on syntax. Why it doesn't work in @registerForReflection is still a mystery to me: https://stackoverflow.com/questions/41207060/how-to-get-a-kclass-of-array
A similar error occurs in Quarkus 1.6.0.CR1 when trying to serialize a built-in Kotlin List, e.g.
@DefaultConstructor
@RegisterForReflection
data class Example(
var name: String
)
@GET
@Path("/example")
fun getListOfExamples(): List<Example> // <-- kotlin.collections.List<Example>
JVM-mode works fine but in native-mode (GraalVM 20.1) the following errors occurs:
[io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /example failed, error id: 66d7f184-0352-42f5-880a-b7792076f675-1: org.jboss.resteasy.spi.UnhandledException: java.lang.IllegalStateException: @NotNull method kotlin/reflect/jvm/internal/impl/builtins/KotlinBuiltIns.getBuiltInClassByFqName must not return null
at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:381)
at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:216)
at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:610)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:520)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:259)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:160)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:163)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:245)
at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:132)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.access$000(VertxRequestHandler.java:37)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:94)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2046)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1578)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
at java.lang.Thread.run(Thread.java:834)
at org.jboss.threads.JBossThread.run(JBossThread.java:479)
at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:517)
at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:193)
Caused by: java.lang.IllegalStateException: @NotNull method kotlin/reflect/jvm/internal/impl/builtins/KotlinBuiltIns.getBuiltInClassByFqName must not return null
at kotlin.reflect.jvm.internal.impl.builtins.KotlinBuiltIns.$$$reportNull$$$0(KotlinBuiltIns.java)
at kotlin.reflect.jvm.internal.impl.builtins.KotlinBuiltIns.getBuiltInClassByFqName(KotlinBuiltIns.java:357)
at kotlin.reflect.jvm.internal.impl.builtins.jvm.JavaToKotlinClassMap.mapJavaToKotlin(JavaToKotlinClassMap.kt:130)
at kotlin.reflect.jvm.internal.impl.builtins.jvm.JavaToKotlinClassMap.mapJavaToKotlin$default(JavaToKotlinClassMap.kt:126)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.types.JavaTypeResolver.mapKotlinClass(JavaTypeResolver.kt:161)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.types.JavaTypeResolver.computeTypeConstructor(JavaTypeResolver.kt:135)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.types.JavaTypeResolver.computeSimpleJavaClassifierType(JavaTypeResolver.kt:117)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.types.JavaTypeResolver.transformJavaClassifierType(JavaTypeResolver.kt:97)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.types.JavaTypeResolver.transformJavaType(JavaTypeResolver.kt:52)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope.resolveValueParameters(LazyJavaScope.kt:215)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassMemberScope.resolveConstructor(LazyJavaClassMemberScope.kt:600)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassMemberScope.access$resolveConstructor(LazyJavaClassMemberScope.kt:67)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassMemberScope$constructors$1.invoke(LazyJavaClassMemberScope.kt:89)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassMemberScope$constructors$1.invoke(LazyJavaClassMemberScope.kt:67)
at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:355)
at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:474)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassDescriptor.getConstructors(LazyJavaClassDescriptor.kt:129)
at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassDescriptor.getConstructors(LazyJavaClassDescriptor.kt:42)
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)
at com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair.findCreatorAnnotation(AnnotationIntrospectorPair.java:804)
at com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair.findCreatorAnnotation(AnnotationIntrospectorPair.java:804)
at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._addCreatorParam(POJOPropertiesCollector.java:498)
at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._addCreators(POJOPropertiesCollector.java:465)
at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.collectAll(POJOPropertiesCollector.java:313)
at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getJsonValueAccessor(POJOPropertiesCollector.java:196)
at com.fasterxml.jackson.databind.introspect.BasicBeanDescription.findJsonValueAccessor(BasicBeanDescription.java:252)
at com.fasterxml.jackson.databind.ser.BasicSerializerFactory.findSerializerByAnnotations(BasicSerializerFactory.java:346)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._createSerializer2(BeanSerializerFactory.java:216)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:165)
at com.fasterxml.jackson.databind.SerializerProvider._createUntypedSerializer(SerializerProvider.java:1388)
at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1356)
at com.fasterxml.jackson.databind.SerializerProvider.findValueSerializer(SerializerProvider.java:551)
at com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase.createContextual(AsArraySerializerBase.java:201)
at com.fasterxml.jackson.databind.SerializerProvider.handleSecondaryContextualization(SerializerProvider.java:1004)
at com.fasterxml.jackson.databind.SerializerProvider.findValueSerializer(SerializerProvider.java:561)
at com.fasterxml.jackson.databind.SerializerProvider.findTypedValueSerializer(SerializerProvider.java:758)
at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.forRootType(ObjectWriter.java:1396)
at com.fasterxml.jackson.databind.ObjectWriter.forType(ObjectWriter.java:403)
at org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider.writeTo(ResteasyJackson2Provider.java:296)
at org.jboss.resteasy.core.messagebody.AsyncBufferedMessageBodyWriter.asyncWriteTo(AsyncBufferedMessageBodyWriter.java:24)
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:404)
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:590)
... 20 more
Looks like something that would be addressed by https://github.com/quarkusio/quarkus/issues/3954#issuecomment-654154905.
@RotBolt do you want to take it?
Just hit this again. Any progress on a fix? Anyway I can help?
Most helpful comment
@geoand will look into this 馃憤