Skip to content

Add Kotlin data class components support to BindingReflectionHintsRegistrar #29316

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

Closed
sdeleuze opened this issue Oct 13, 2022 · 3 comments
Closed
Assignees
Labels
theme: aot An issue related to Ahead-of-time processing theme: kotlin An issue related to Kotlin support type: enhancement A general enhancement
Milestone

Comments

@sdeleuze
Copy link
Contributor

BindingReflectionHintsRegistrar currently does not add reflection entries for Kotlin data class components.

With this kind of repository and data class:

data class Customer(@Id val id: Int?, val name: String)
interface CustomerRepository : CoroutineCrudRepository<Customer, Int>

The following error is visible at startup:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerRepository': Could not compute caller for function: public final operator fun component1(): kotlin.Int? defined in com.example.demo.Customer[DeserializedSimpleFunctionDescriptor@32208e3c] (member = null)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1753) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1375) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1295) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:348) ~[na:na]
        ... 21 common frames omitted
Caused by: kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Could not compute caller for function: public final operator fun component1(): kotlin.Int? defined in com.example.demo.Customer[DeserializedSimpleFunctionDescriptor@32208e3c] (member = null)
        at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:88) ~[na:na]
        at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:61) ~[na:na]
        at kotlin.reflect.jvm.internal.ReflectProperties$LazyVal.invoke(ReflectProperties.java:63) ~[na:na]
        at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32) ~[demo:1.7.20-release-201(1.7.20)]
        at kotlin.reflect.jvm.internal.KFunctionImpl.getCaller(KFunctionImpl.kt:61) ~[na:na]
        at kotlin.reflect.jvm.ReflectJvmMapping.getJavaMethod(ReflectJvmMapping.kt:63) ~[na:na]
        at kotlin.reflect.jvm.ReflectJvmMapping.getKotlinFunction(ReflectJvmMapping.kt:136) ~[na:na]
        at org.springframework.core.MethodParameter$KotlinDelegate.getGenericReturnType(MethodParameter.java:914) ~[na:na]
        at org.springframework.core.MethodParameter.getGenericParameterType(MethodParameter.java:510) ~[demo:6.0.0-RC1]
        at org.springframework.core.SerializableTypeWrapper$MethodParameterTypeProvider.getType(SerializableTypeWrapper.java:291) ~[na:na]
        at org.springframework.core.SerializableTypeWrapper.forTypeProvider(SerializableTypeWrapper.java:107) ~[na:na]
        at org.springframework.core.ResolvableType.forType(ResolvableType.java:1413) ~[demo:6.0.0-RC1]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1334) ~[demo:6.0.0-RC1]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1316) ~[demo:6.0.0-RC1]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1283) ~[demo:6.0.0-RC1]
        at org.springframework.core.ResolvableType.forMethodReturnType(ResolvableType.java:1243) ~[demo:6.0.0-RC1]
        at org.springframework.core.GenericTypeResolver.resolveReturnType(GenericTypeResolver.java:80) ~[na:na]
        at org.springframework.beans.GenericTypeAwarePropertyDescriptor.<init>(GenericTypeAwarePropertyDescriptor.java:110) ~[na:na]
        at org.springframework.beans.CachedIntrospectionResults.buildGenericTypeAwarePropertyDescriptor(CachedIntrospectionResults.java:431) ~[na:na]
        at org.springframework.beans.CachedIntrospectionResults.<init>(CachedIntrospectionResults.java:310) ~[na:na]
        at org.springframework.beans.CachedIntrospectionResults.forClass(CachedIntrospectionResults.java:183) ~[na:na]
        at org.springframework.beans.BeanUtils.getPropertyDescriptors(BeanUtils.java:489) ~[na:na]
        at org.springframework.data.mapping.context.AbstractMappingContext.doAddPersistentEntity(AbstractMappingContext.java:414) ~[demo:3.0.0-SNAPSHOT]

It should be tested in https://github.com/spring-projects/spring-aot-smoke-tests and work out of the box.

@sdeleuze sdeleuze added type: enhancement A general enhancement theme: aot An issue related to Ahead-of-time processing theme: kotlin An issue related to Kotlin support labels Oct 13, 2022
@sdeleuze sdeleuze added this to the 6.0.0-RC2 milestone Oct 13, 2022
@sdeleuze sdeleuze self-assigned this Oct 13, 2022
@cmdjulian
Copy link

I also did encounter the problem for a for two other situations:

Mind the following controller:

@RestController
@RequestMapping(NOTE_ENDPOINT)
class NoteController(private val service: NoteService) {

    @PostMapping
    fun createNote(@PathVariable notebookId: UUID, @Valid @RequestBody command: NoteCommand): NoteDto {
        return service.createNote(command.toNote(notebookId), command.tags).toDto()
    }

    @GetMapping
    fun findNotes(@PathVariable notebookId: UUID, filters: NoteFilters): List<NoteDto> {
        return service.findNotes(notebookId, filters.tagNames, filters.content).map(Note::toDto)
    }
}

data class NoteCommand(

    @get:Size(min = 3, max = 128)
    val title: String,

    @get:Size(max = 65536)
    val content: String?,

    val tags: List<String> = emptyList()

)

data class NoteFilters(val tagNames: List<String>?, val content: String?)

Sending a valid NoteCommand works as expected. But if some of the constraints are violated, an error is raised resulting in a 500 server error, as the previously described problem is triggered as well.

Secondly, requesting the collection endpoint, having a container object for a bunch of request parameters, in my case NoteFilters, also causes the application to respond with a 500 server error because of missing reflection config.

Bot can be prevented by registering the aforementioned types in the reflect config, something like that works:

class AotConfig : RuntimeHintsRegistrar {
    override fun registerHints(hints: RuntimeHints, classLoader: ClassLoader?) {
        hints.reflection().registerType(NoteCommand::class.java, *MemberCategory.values())
        hints.reflection().registerType(NoteFilters::class.java, *MemberCategory.values())
    }
}

with src/main/resources/META-INF/spring:

org.springframework.aot.hint.RuntimeHintsRegistrar=com.example.app.config.AotConfig

@sdeleuze
Copy link
Contributor Author

@cmdjulian Could be a different issue. I have created #29331 for something I have been able to reproduce. Please create another issue if you see other problems and please add a repro prject + the exception you see.

@cmdjulian
Copy link

Okay, will do, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
theme: aot An issue related to Ahead-of-time processing theme: kotlin An issue related to Kotlin support type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants