Skip to content

Issue with AOT and Kotlin data class: The following must be non-sealed interfaces #4351

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
fab918 opened this issue Mar 29, 2023 · 7 comments
Assignees
Labels
in: kotlin Kotlin support theme: aot An issue related to Ahead-Of-Time processing type: bug A general bug

Comments

@fab918
Copy link

fab918 commented Mar 29, 2023

When I add the gradle AOT plugin id("org.graalvm.buildtools.native") I have an error on a simple data class document from org.springframework.data.mongodb.aot.LazyLoadingProxyAotProcessor:

Exception in thread "main" java.lang.IllegalArgumentException: The following must be non-sealed interfaces: [...persistence.entity.Tags]
	at org.springframework.aot.hint.JdkProxyHint$Builder.toTypeReferences(JdkProxyHint.java:159)
	at org.springframework.aot.hint.JdkProxyHint$Builder.proxiedInterfaces(JdkProxyHint.java:129)
	at org.springframework.aot.hint.ProxyHints.lambda$registerJdkProxy$1(ProxyHints.java:78)
	at org.springframework.aot.hint.ProxyHints.registerJdkProxy(ProxyHints.java:50)
	at org.springframework.aot.hint.ProxyHints.registerJdkProxy(ProxyHints.java:77)
	at org.springframework.data.mongodb.aot.LazyLoadingProxyAotProcessor.lambda$registerLazyLoadingProxyIfNeeded$2(LazyLoadingProxyAotProcessor.java:76)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
	at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1845)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at org.springframework.data.mongodb.aot.LazyLoadingProxyAotProcessor.registerLazyLoadingProxyIfNeeded(LazyLoadingProxyAotProcessor.java:55)
	at org.springframework.data.mongodb.aot.MongoManagedTypesBeanRegistrationAotProcessor.contributeType(MongoManagedTypesBeanRegistrationAotProcessor.java:54)
	at org.springframework.data.aot.ManagedTypesRegistrationAotContribution.lambda$applyTo$0(ManagedTypesRegistrationAotContribution.java:97)
	at org.springframework.data.util.TypeCollector.processType(TypeCollector.java:103)
	at org.springframework.data.util.TypeCollector.process(TypeCollector.java:90)
	at org.springframework.data.util.TypeCollector$ReachableTypes.lambda$forEach$0(TypeCollector.java:229)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.springframework.data.util.TypeCollector$ReachableTypes.forEach(TypeCollector.java:229)
	at org.springframework.data.aot.ManagedTypesRegistrationAotContribution.applyTo(ManagedTypesRegistrationAotContribution.java:97)
	at org.springframework.beans.factory.aot.BeanDefinitionMethodGenerator.lambda$generateBeanDefinitionMethod$2(BeanDefinitionMethodGenerator.java:186)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.springframework.beans.factory.aot.BeanDefinitionMethodGenerator.generateBeanDefinitionMethod(BeanDefinitionMethodGenerator.java:186)
	at org.springframework.beans.factory.aot.BeanDefinitionMethodGenerator.generateBeanDefinitionMethod(BeanDefinitionMethodGenerator.java:109)
	at org.springframework.beans.factory.aot.BeanRegistrationsAotContribution.lambda$generateRegisterBeanDefinitionsMethod$2(BeanRegistrationsAotContribution.java:85)
	at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721)
	at org.springframework.beans.factory.aot.BeanRegistrationsAotContribution.generateRegisterBeanDefinitionsMethod(BeanRegistrationsAotContribution.java:83)
	at org.springframework.beans.factory.aot.BeanRegistrationsAotContribution.lambda$applyTo$1(BeanRegistrationsAotContribution.java:67)
	at org.springframework.aot.generate.GeneratedMethod.<init>(GeneratedMethod.java:54)
	at org.springframework.aot.generate.GeneratedMethods.add(GeneratedMethods.java:112)
	at org.springframework.aot.generate.GeneratedMethods.add(GeneratedMethods.java:89)
	at org.springframework.beans.factory.aot.BeanRegistrationsAotContribution.applyTo(BeanRegistrationsAotContribution.java:66)
	at org.springframework.context.aot.BeanFactoryInitializationAotContributions.applyTo(BeanFactoryInitializationAotContributions.java:78)
	at org.springframework.context.aot.ApplicationContextAotGenerator.lambda$processAheadOfTime$0(ApplicationContextAotGenerator.java:58)
	at org.springframework.context.aot.ApplicationContextAotGenerator.withCglibClassHandler(ApplicationContextAotGenerator.java:67)
	at org.springframework.context.aot.ApplicationContextAotGenerator.processAheadOfTime(ApplicationContextAotGenerator.java:53)
	at org.springframework.context.aot.ContextAotProcessor.performAotProcessing(ContextAotProcessor.java:106)
	at org.springframework.context.aot.ContextAotProcessor.doProcess(ContextAotProcessor.java:84)
	at org.springframework.context.aot.ContextAotProcessor.doProcess(ContextAotProcessor.java:49)
	at org.springframework.context.aot.AbstractAotProcessor.process(AbstractAotProcessor.java:82)
	at org.springframework.boot.SpringApplicationAotProcessor.main(SpringApplicationAotProcessor.java:80)

FAILURE: Build failed with an exception.

The Tags class is a simple data class:

@Document
data class Tags(
    @Id var key: String = "",
    var url: String = "",
    var langs: Map<LangEnum, String> = emptyMap()
)

Here is the gradle.kts plugin file, note that to use dataclass with spring data mongodb, you already have to open these classes with allOpen, I guess it is probably related.

plugins {
    id("org.springframework.boot") version "3.0.5"
    id("io.spring.dependency-management") version "1.1.0"
    id("org.jetbrains.kotlin.plugin.allopen") version "1.8.20-RC2"
    id("org.graalvm.buildtools.native") version "0.9.20"
    kotlin("jvm") version "1.7.22"
    kotlin("plugin.spring") version "1.7.22"
}

allOpen {
    annotation("org.springframework.data.mongodb.core.mapping.Document")
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 29, 2023
@christophstrobl
Copy link
Member

@fab918 thank you for reporting !
Seems the LazyLoadingProxyAotProcessor has detected a lazy reference pointing to the Tags type that is causing the error.
Could you please take the time to provide a complete minimal sample (something that we can unzip or git clone, build, and deploy) that reproduces the problem and includes also the referencing side of the relationship that is pointing to Tags.

@christophstrobl christophstrobl self-assigned this Mar 29, 2023
@christophstrobl christophstrobl added status: waiting-for-feedback We need additional information before we can continue theme: aot An issue related to Ahead-Of-Time processing in: kotlin Kotlin support and removed status: waiting-for-triage An issue we've not yet triaged labels Mar 29, 2023
@fab918
Copy link
Author

fab918 commented Mar 29, 2023

@christophstrobl thanks for your quick answer!
I was able to reproduce the problem in an almost empty project, just the Tags class and the referencing class. (I tried with GraalVm 17.0.4 and 17.0.5 for info).

I noticed a strange thing, if the lazy is an object of type Tags, there is no problem, however if it is a list it is a problem.

@Document
data class Article(
    @Id var id: String = ObjectId.get().toString(),
    // @DBRef(lazy = true) var topic: Tags // This works
    @DBRef(lazy = true) var tags: List<Tags> = emptyList(), // This produce the AOT error
)

aotKotlinBugSealed.zip

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Mar 29, 2023
@christophstrobl
Copy link
Member

Thanks for the reproducer. I need to have a deeper look at this.
Looking at the byte code everything is referring to java.util.List for the tags property. Also constructors, getters/setters and kotlin copy methods show no indication why it should not work.

@fab918
Copy link
Author

fab918 commented Mar 29, 2023

I am surprised to be the first to encounter the issue, if you need anything don't hesitate to ask!

FYI, I tried to convert both entities and the Application class to Java, and the problem still occurs. However, if the project is a full Java project (I'm attaching it), in this case there is no problem. I hope this will give you a clue about the cause.

demo-java.zip

@christophstrobl
Copy link
Member

christophstrobl commented Mar 31, 2023

Thanks for the reproducer! It looks like the type resolution needs a little filtering when collecting the types for the proxy signature.

@christophstrobl
Copy link
Member

ok, so there's even more hidden once the AOT compilation step is fixed.
The generated proxy-config is slightly off.

required

[0]: interface java.util.Collection, 
[1]: interface org.springframework.data.mongodb.core.convert.LazyLoadingProxy, 
[2]: interface java.util.List, 
[3]: interface org.springframework.aop.SpringProxy, 
[4]: interface org.springframework.aop.framework.Advised, 
[5]: interface org.springframework.core.DecoratingProxy

generated

[0]: interface org.springframework.data.mongodb.core.convert.LazyLoadingProxy
[1]: interface java.util.List
[2]: interface java.util.Collection 
[3]: interface java.util.Iterable
[4]: interface org.springframework.aop.SpringProxy
[5]: interface org.springframework.aop.framework.Advised
[6]: interface org.springframework.core.DecoratingProxy

@fab918
Copy link
Author

fab918 commented Mar 31, 2023

Great! Thank you for your work!

mp911de added a commit that referenced this issue Apr 13, 2023
Tweak naming.

Original pull request: #4352
See #4351
mp911de pushed a commit that referenced this issue Apr 13, 2023
This commit makes sure to use the ProxyFactory for retrieving the proxied interfaces. This makes sure to capture the exact interface order required when finally loading the proxy at runtime.

Original pull request: #4352
Closes #4351
mp911de added a commit that referenced this issue Apr 13, 2023
Tweak naming.

Original pull request: #4352
See #4351
@mp911de mp911de added this to the 4.0.5 (2022.0.5) milestone Apr 13, 2023
@mp911de mp911de added type: bug A general bug and removed status: feedback-provided Feedback has been provided labels Apr 13, 2023
@mp911de mp911de linked a pull request Apr 13, 2023 that will close this issue
wilkinsona pushed a commit to spring-projects/spring-aot-smoke-tests that referenced this issue Apr 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: kotlin Kotlin support theme: aot An issue related to Ahead-Of-Time processing type: bug A general bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants