Skip to content
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

Native image compilation failure on jooq.runtime.graal.DefaultRecordMapperSubstitutions.proxy #177

Open
laurentperez opened this issue Oct 29, 2023 · 8 comments

Comments

@laurentperez
Copy link

laurentperez commented Oct 29, 2023

Hello

Using latest tag 2.0.0 and quarkusPlatformVersion=3.4.3

Using container build (with Mandrel) :

./gradlew build -Dquarkus.package.type=native -Dquarkus.native.container-build=true -Dquarkus.container-image.build=true

Native compilation fails on graalvm side :


Status: Downloaded newer image for quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-17

<==========---> 80% EXECUTING [3m 4s]
========================================================================================================================
GraalVM Native Image: Generating 'xxxxxxxxxxxx' (executable)...
========================================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
------------------------------------------------------------------------------------------------------------------------

[1/8] Initializing...                                                                                    (0,0s @ 0,29GB)
Error: Could not find target method: private java.lang.Object io.quarkiverse.jooq.runtime.graal.DefaultRecordMapperSubstitutions.proxy()
com.oracle.svm.core.util.UserError$UserException: Could not find target method: private java.lang.Object io.quarkiverse.jooq.runtime.graal.DefaultRecordMapperSubstitutions.proxy()
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.util.UserError.abort(UserError.java:73)
        at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor.findOriginalMethod(AnnotationSubstitutionProcessor.java:872)
        at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor.handleMethodInAliasClass(AnnotationSubstitutionProcessor.java:463)
        at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor.handleAliasClass(AnnotationSubstitutionProcessor.java:427)
        at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor.handleClass(AnnotationSubstitutionProcessor.java:400)

Stacktrace side :

> There was a failure while executing work items
   > A failure occurred while executing io.quarkus.gradle.tasks.worker.BuildWorker
      > io.quarkus.builder.BuildException: Build failure: Build failed due to errors
                [error]: Build step io.quarkus.deployment.pkg.steps.NativeImageBuildStep#build threw an exception: io.quarkus.deployment.pkg.steps.NativeImageBuildStep$ImageGenerationFailureException: Image generation failed. Exit code: 1
                at io.quarkus.deployment.pkg.steps.NativeImageBuildStep.imageGenerationFailed(NativeImageBuildStep.java:457)
                at io.quarkus.deployment.pkg.steps.NativeImageBuildStep.build(NativeImageBuildStep.java:263)
                at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
                at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
                at java.base/java.lang.reflect.Method.invoke(Method.java:568)
                at io.quarkus.deployment.ExtensionLoader$3.execute(ExtensionLoader.java:858)
                at io.quarkus.builder.BuildContext.run(BuildContext.java:282)
@laurentperez laurentperez changed the title Native image compilation failure on ooq.runtime.graal.DefaultRecordMapperSubstitutions.proxy Native image compilation failure on jooq.runtime.graal.DefaultRecordMapperSubstitutions.proxy Oct 29, 2023
@laurentperez
Copy link
Author

Digging, this is related to unmatchable subsitution of

image

in jOOQ/jOOQ#15482

@laurentperez
Copy link
Author

Digging further, since proxy() is now Reflect.on I simply did a crude

rm runtime/src/main/java/io/quarkiverse/jooq/runtime/graal/DefaultRecordMapperSubstitutions.java

and rebuilt a 999-SNAPSHOT. It solves the compilation error for @Substitute however it fails later deep in JOOQ itself.

"This error is reported at image build time because class org.jooq.impl.Tools is registered for linking at image build time by command line"

It seems related to jakarta.persistence. classes graph. See Column, Entity classes :

[2/8] Performing analysis... [] (33,5s @ 0,78GB)
8 087 (70,52%) of 11 468 types reachable
9 198 (49,59%) of 18 548 fields reachable
28 869 (28,25%) of 102 195 methods reachable
3 002 types, 1 679 fields, and 7 555 methods registered for reflection
2 fatal errors detected:

Fatal error: com.oracle.graal.pointsto.util.AnalysisError$ParsingError: Error encountered while parsing org.jooq.impl.Tools$$Lambda$1568/0x00000007c262fba8.get(Unknown Source) 
Parsing context:
   at org.jooq.impl.Cache.run(Cache.java:103)
   at org.jooq.impl.Tools.getMatchingGetter(Tools.java:4458)
   at org.jooq.impl.DefaultRecordMapper$ImmutablePOJOMapper.<init>(DefaultRecordMapper.java:1057)
   at org.jooq.impl.DefaultRecordMapper.init(DefaultRecordMapper.java:568)
   at org.jooq.impl.DefaultRecordMapper.<init>(DefaultRecordMapper.java:361)
Caused by: org.graalvm.compiler.java.BytecodeParser$BytecodeParserError: com.oracle.graal.pointsto.constraints.UnresolvedElementException: Discovered unresolved type during parsing: jakarta.persistence.Column. This error is reported at image build time because class org.jooq.impl.Tools is registered for linking at image build time by command line
        at parsing org.jooq.impl.Tools.lambda$getAnnotatedGetter$100(Tools.java:4371)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.throwParserError(BytecodeParser.java:2536)
        at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.phases.SharedGraphBuilderPhase$SharedBytecodeParser.throwParserError(SharedGraphBuilderPhase.java:169)
Caused by: com.oracle.graal.pointsto.constraints.UnresolvedElementException: Discovered unresolved type during parsing: jakarta.persistence.Entity. This error is reported at image build time because class org.jooq.impl.Tools is registered for linking at image build time by command line
        at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.phases.SharedGraphBuilderPhase$SharedBytecodeParser.reportUnresolvedElement(SharedGraphBuilderPhase.java:521)
        at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.phases.SharedGraphBuilderPhase$SharedBytecodeParser.reportUnresolvedElement(SharedGraphBuilderPhase.java:515)

@laurentperez
Copy link
Author

Latest comments after jOOQ/jOOQ#8779 (comment) suggest JOOQ GraalVM support is planned for JOOQ 3.19.x and above

I'm not sure if the root UnresolvedElementException cause of the error is related to JOOQ referencing JPA itself (Entity, Column, etc.) .

Should the quarkus-jooq plugin import these JPA classes to let resolution happen at graph construction ?

@laurentperez
Copy link
Author

I included jakarta.persistence as a dependency by adding a crude import of

implementation("io.quarkus:quarkus-hibernate-orm")

Then graph analysis phase [2/8] Performing analysis works 🥳

I'll make a proper PR without depending on H8 for JPA

@laurentperez
Copy link
Author

please note that analysis and compilation work, but runtime will fail because Records are not declared for reflection

micronaut solved this thru https://github.com/micronaut-projects/micronaut-sql/blob/master/src/main/docs/guide/jooq/jooq-graalvm.adoc and https://github.com/micronaut-projects/micronaut-sql/tree/master/jooq ( micronaut-projects/micronaut-sql#851 )

@laurentperez
Copy link
Author

I can't pinpoint how to tell Quarkus how to generate reflection configuration for JOOQ Records

The JOOQ gradle plugin successfully generates the Jakarta Persistence API annotations but when the native binary is run, these annotated Records don't exist in the closed world "classpath", I get linkage errors (at runtime, not compile/build time)

I understand Micronaut-sql looks for annotations and generates an @Introspected annotation for Records, which itself is scanned at compile time and generates ReflectionConfiguration for graalvm

AFAIK Quarkus has a slightly different approach where it scans classes and generates a bytecode metamodel, and this metamodel generates the reflection configuration

I was expecting QK to work "out of the box" and generate the metamodel for JOOQ Records annotated with JPA. It does not so I'm attempting to do a PR for quarkus-jooq, but I don't see where my "missing link" between a Record and its reflection configuration is. I'm no QK expert ;)

Basically I want to augment the QK scanning process and tell it to add reflection for all public fields + constructor on JOOQ Records annotated with JPA

I'm not sure if this should be a build time thing as in https://github.com/quarkiverse/quarkus-jooq/blob/main/deployment/src/main/java/io/quarkiverse/jooq/deployment/JooqProcessor.java or a runtime one as in https://github.com/quarkiverse/quarkus-jooq/tree/main/runtime/src/main/java/io/quarkiverse/jooq/runtime

I believe the answer might be in https://github.com/quarkusio/quarkus/tree/main/extensions/hibernate-orm code because JPA-annotated H8 entities work, but I'm not sure.

Or it could be simpler and simply adding a @RegisterForReflection to the generated Records, avoiding generation of a per-record reflect-config.json. Is it the proper way to do it ?

@ranjanashish or @angrymango would you have tips on how to do it ?

@Duvel
Copy link
Contributor

Duvel commented Jan 29, 2024

I can confirm that removing or overruling DefaultRecordMapperSubstitutions.java does help, so I guess it can be removed from the source.

@laurentperez the issues with jakarta and the reflection didn't occur for my project, so maybe this is more due to the use of micronaut?

My project does use the records in the code and that way they get registered for reflection.

@Duvel Duvel mentioned this issue Jan 29, 2024
@Duvel
Copy link
Contributor

Duvel commented Jan 29, 2024

I created a pull request for removing DefaultRecordMapperSubstitutions.java #188.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants