Skip to content

@Configuration classes with Kotlin in native-image are broken #29663

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
mhalbritter opened this issue Dec 8, 2022 · 2 comments
Closed

@Configuration classes with Kotlin in native-image are broken #29663

mhalbritter opened this issue Dec 8, 2022 · 2 comments
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

@mhalbritter
Copy link
Contributor

mhalbritter commented Dec 8, 2022

This happens only in a native image. I have this configuration:

@Configuration(proxyBeanMethods = false)
class MyConfiguration {
    @Bean
    fun a(): A {
        return A()
    }

    private fun b(): B {
        return B()
    }
}

class A

class B

Running the application works. Now rename the a() method to z_a() and it breaks:

java.lang.ClassNotFoundException: com.example.demo.B
        at [email protected]/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:52) ~[demo:na]
        at [email protected]/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na]
        at [email protected]/java.lang.ClassLoader.loadClass(ClassLoader.java:132) ~[demo:na]
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.parseType(KDeclarationContainerImpl.kt:273) ~[demo:1.7.21-release-272(1.7.21)]
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.loadReturnType(KDeclarationContainerImpl.kt:288) ~[demo:1.7.21-release-272(1.7.21)]
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.findMethodBySignature(KDeclarationContainerImpl.kt:198) ~[demo:1.7.21-release-272(1.7.21)]
        at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:68) ~[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.21-release-272(1.7.21)]
        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) ~[na:na]
        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.2]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1334) ~[demo:6.0.2]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1316) ~[demo:6.0.2]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1283) ~[demo:6.0.2]
        at org.springframework.core.ResolvableType.forMethodReturnType(ResolvableType.java:1228) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:814) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:681) ~[demo:6.0.2]
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:652) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1632) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:559) ~[demo:6.0.2]
        at org.springframework.util.ReflectionUtils.rethrowRuntimeException(ReflectionUtils.java:147)
        at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:794)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291)
        at com.example.demo.DemoApplicationKt.main(DemoApplication.kt:13)
Caused by: java.lang.ClassNotFoundException: com.example.demo.B
        at [email protected]/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:52)
        at [email protected]/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at [email protected]/java.lang.ClassLoader.loadClass(ClassLoader.java:132)
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.parseType(KDeclarationContainerImpl.kt:273)
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.loadReturnType(KDeclarationContainerImpl.kt:288)
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.findMethodBySignature(KDeclarationContainerImpl.kt:198)
        at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:68)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:531) ~[demo:6.0.2]
        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:106) ~[na:na]
        at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:745) ~[demo:6.0.2]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:565) ~[demo:6.0.2]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432) ~[demo:3.0.0]
        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.ReflectJvmMapping.getJavaMethod(ReflectJvmMapping.kt:63)
        at kotlin.reflect.jvm.ReflectJvmMapping.getKotlinFunction(ReflectJvmMapping.kt:136)
        at org.springframework.core.MethodParameter$KotlinDelegate.getGenericReturnType(MethodParameter.java:914)
        at org.springframework.core.MethodParameter.getGenericParameterType(MethodParameter.java:510)
        at org.springframework.core.SerializableTypeWrapper$MethodParameterTypeProvider.getType(SerializableTypeWrapper.java:291)
        at org.springframework.core.SerializableTypeWrapper.forTypeProvider(SerializableTypeWrapper.java:107)
        at org.springframework.core.ResolvableType.forType(ResolvableType.java:1413)
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1334)
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1316)
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1283)
        at org.springframework.core.ResolvableType.forMethodReturnType(ResolvableType.java:1228)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:814)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:681)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:652)
        at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1632)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:559)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:531)
        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:106)
        at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:745)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:565)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
        ... 3 more
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[demo:3.0.0]
        at com.example.demo.DemoApplicationKt.main(DemoApplication.kt:13) ~[demo:na]

2022-12-08T17:30:40.001+01:00  WARN 179946 --- [           main] o.s.boot.SpringApplication               : Unable to close ApplicationContext

java.lang.ClassNotFoundException: com.example.demo.B
        at [email protected]/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:52) ~[demo:na]
        at [email protected]/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na]
        at [email protected]/java.lang.ClassLoader.loadClass(ClassLoader.java:132) ~[demo:na]
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.parseType(KDeclarationContainerImpl.kt:273) ~[demo:1.7.21-release-272(1.7.21)]
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.loadReturnType(KDeclarationContainerImpl.kt:288) ~[demo:1.7.21-release-272(1.7.21)]
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.findMethodBySignature(KDeclarationContainerImpl.kt:198) ~[demo:1.7.21-release-272(1.7.21)]
        at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:68) ~[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.21-release-272(1.7.21)]
        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) ~[na:na]
        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.2]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1334) ~[demo:6.0.2]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1316) ~[demo:6.0.2]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1283) ~[demo:6.0.2]
        at org.springframework.core.ResolvableType.forMethodReturnType(ResolvableType.java:1228) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:814) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:681) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:652) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1632) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:559) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:531) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:658) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:650) ~[demo:6.0.2]
        at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1276) ~[demo:6.0.2]
        at org.springframework.boot.SpringApplication.getExitCodeFromMappedException(SpringApplication.java:863) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.getExitCodeFromException(SpringApplication.java:851) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.handleExitCode(SpringApplication.java:838) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:778) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:321) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[demo:3.0.0]
        at com.example.demo.DemoApplicationKt.main(DemoApplication.kt:13) ~[demo:na]

Demo project here: demo.zip

I debugged that to a behaviour in Kotlin reflection. Essentially you need reflection metadata for ALL methods (not only for the @Bean methods like it's at the moment) if you use ResolvableType.forMethodReturnType(method). It depends on the iteration order of the methods. You can read more here: https://github.com/mhalbritter/kotlin-graalvm-reflection

@mhalbritter mhalbritter changed the title @Configuration classes with Kotlin are broken @Configuration classes with Kotlin in native-image are broken Dec 8, 2022
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Dec 8, 2022
@sbrannen sbrannen added theme: aot An issue related to Ahead-of-time processing theme: kotlin An issue related to Kotlin support labels Dec 8, 2022
@sbrannen sbrannen added this to the Triage Queue milestone Dec 8, 2022
@sdeleuze sdeleuze self-assigned this Jan 2, 2023
@sdeleuze sdeleuze removed the status: waiting-for-triage An issue we've not yet triaged or decided on label Jan 2, 2023
@sdeleuze sdeleuze modified the milestones: Triage Queue, 6.0.x Jan 2, 2023
@sdeleuze
Copy link
Contributor

sdeleuze commented Jan 2, 2023

Confirmed, I have bring this behavior to the attention of the Kotlin team and will share their feedback here. Worst case scenario, I guess we could configure introspect medadata for all functions of Kotlin classes.

@sdeleuze sdeleuze modified the milestones: 6.0.x, 6.0.4 Jan 2, 2023
@sdeleuze
Copy link
Contributor

sdeleuze commented Jan 9, 2023

Feedback from Kotlin team:

The implementation can be optimized somewhat, but in the worst case we still have to iterate over all methods in a class, because the rules of correspondence between KFunction <-> java.lang.reflect.Method are not trivial and even their names don't match sometimes (e.g. because of @JvmName, default parameters, inline classes, etc).

So let's configure introspection on all functions for Kotlin classes.

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

4 participants