GraalVM native-image fails with minimum configuration
shainegordon opened this issue · comments
I'm very new to GraalVM, so I am hoping this is a simple fix, with some better understanding.
I was having issues with GraalVM and Groovy dependencies while using version nz.net.ultraq.thymeleaf.thymeleaf-layout-dialect@3.2.0
, on step 7 - compiling
I then found this issue - #232 - which alerted me to the existence of 3.2.1
However, this doesnt actually allow this lib to be used in a project that wants to use GraalVM.
Quite easy to reproduce, and I have pushed to a demo repository - https://github.com/shainegordon/thymleaf_layout_graalvm
This can be reproduced by running either
docker build -t thymleaf_layout_graalvm .
OR
./mvnw clean native:compile -Pnative
This is a minimal project, created at https://start.spring.io/, with only "Web" and "Native" added. Kotlin, Maven, Java 17, Spring boot 3.0.5.
DemoApplication.kt
package com.example.demo
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<kotlin.version>1.8.10</kotlin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring6</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>${hibernate.version}</version>
<executions>
<execution>
<id>enhance</id>
<goals>
<goal>enhance</goal>
</goals>
<configuration>
<enableLazyInitialization>true</enableLazyInitialization>
<enableDirtyTracking>true</enableDirtyTracking>
<enableAssociationManagement>true</enableAssociationManagement>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
<plugin>jpa</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-noarg</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
This will basically produce the following output/error
GraalVM Native Image: Generating 'demo' (executable)...
========================================================================================================================
[1/7] Initializing... (5,6s @ 0,32GB)
Version info: 'GraalVM 22.3.1 Java 17 CE'
Java version info: '17.0.6+10-jvmci-22.3-b13'
C compiler: gcc (linux, x86_64, 11.3.0)
Garbage collector: Serial GC
2 user-specific feature(s)
- com.oracle.svm.polyglot.groovy.GroovyIndyInterfaceFeature
- org.springframework.aot.nativex.feature.PreComputeFieldFeature
Field org.apache.commons.logging.LogAdapter#log4jSpiPresent set to true at build time
Field org.apache.commons.logging.LogAdapter#log4jSlf4jProviderPresent set to true at build time
Field org.apache.commons.logging.LogAdapter#slf4jSpiPresent set to true at build time
Field org.apache.commons.logging.LogAdapter#slf4jApiPresent set to true at build time
Field org.springframework.core.KotlinDetector#kotlinPresent set to true at build time
Field org.springframework.core.KotlinDetector#kotlinReflectPresent set to true at build time
Field org.springframework.core.NativeDetector#imageCode set to true at build time
Field org.springframework.format.support.DefaultFormattingConversionService#jsr354Present set to false at build time
Field org.springframework.cglib.core.AbstractClassGenerator#imageCode set to true at build time
[2/7] Performing analysis... [] (8,5s @ 3,78GB)
9 047 (82,33%) of 10 989 classes reachable
12 287 (66,39%) of 18 507 fields reachable
37 945 (49,30%) of 76 964 methods reachable
612 classes, 363 fields, and 3 625 methods registered for reflection
Error: Classes that should be initialized at run time got initialized during image building:
java.beans.Introspector was unintentionally initialized at build time. To see why java.beans.Introspector got initialized use --trace-class-initialization=java.beans.Introspector
To see how the classes got initialized, use --trace-class-initialization=java.beans.Introspector
Error: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception
If we include --trace-class-initialization=java.beans.Introspector
(by manually running the native-image
command that maven will echo), then we get the following:
GraalVM Native Image: Generating 'demo' (executable)...
========================================================================================================================
[1/7] Initializing... (6,0s @ 0,32GB)
Version info: 'GraalVM 22.3.1 Java 17 CE'
Java version info: '17.0.6+10-jvmci-22.3-b13'
C compiler: gcc (linux, x86_64, 11.3.0)
Garbage collector: Serial GC
2 user-specific feature(s)
- com.oracle.svm.polyglot.groovy.GroovyIndyInterfaceFeature
- org.springframework.aot.nativex.feature.PreComputeFieldFeature
Field org.springframework.core.NativeDetector#imageCode set to true at build time
Field org.apache.commons.logging.LogAdapter#log4jSpiPresent set to true at build time
Field org.apache.commons.logging.LogAdapter#log4jSlf4jProviderPresent set to true at build time
Field org.apache.commons.logging.LogAdapter#slf4jSpiPresent set to true at build time
Field org.apache.commons.logging.LogAdapter#slf4jApiPresent set to true at build time
Field org.springframework.core.KotlinDetector#kotlinPresent set to true at build time
Field org.springframework.core.KotlinDetector#kotlinReflectPresent set to true at build time
Field org.springframework.format.support.DefaultFormattingConversionService#jsr354Present set to false at build time
Field org.springframework.cglib.core.AbstractClassGenerator#imageCode set to true at build time
[2/7] Performing analysis... [] (8,8s @ 4,07GB)
8 959 (82,17%) of 10 903 classes reachable
12 221 (66,47%) of 18 385 fields reachable
37 732 (49,30%) of 76 543 methods reachable
612 classes, 363 fields, and 3 624 methods registered for reflection
Error: Classes that should be initialized at run time got initialized during image building:
java.beans.Introspector was unintentionally initialized at build time. groovy.lang.Closure caused initialization of this class with the following trace:
at java.beans.Introspector.<clinit>(Introspector.java:136)
at groovy.lang.MetaClassImpl.lambda$addProperties$25(MetaClassImpl.java:3464)
at groovy.lang.MetaClassImpl$$Lambda$1474/0x00000007c1ccc6b0.run(Unknown Source)
at java.security.AccessController.executePrivileged(AccessController.java:807)
at java.security.AccessController.doPrivileged(AccessController.java:569)
at groovy.lang.MetaClassImpl.doPrivileged(MetaClassImpl.java:3496)
at groovy.lang.MetaClassImpl.addProperties(MetaClassImpl.java:3464)
at groovy.lang.MetaClassImpl.reinitialize(MetaClassImpl.java:3446)
at groovy.lang.MetaClassImpl.initialize(MetaClassImpl.java:3439)
at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.<init>(MetaClassRegistryImpl.java:142)
at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.<init>(MetaClassRegistryImpl.java:94)
at groovy.lang.GroovySystem.<clinit>(GroovySystem.java:37)
at org.codehaus.groovy.runtime.InvokerHelper.<clinit>(InvokerHelper.java:71)
at groovy.lang.GroovyObjectSupport.getDefaultMetaClass(GroovyObjectSupport.java:46)
at groovy.lang.GroovyObjectSupport.<init>(GroovyObjectSupport.java:32)
at groovy.lang.Closure.<init>(Closure.java:215)
at groovy.lang.Closure.<init>(Closure.java:232)
at groovy.lang.Closure$1.<init>(Closure.java:197)
at groovy.lang.Closure.<clinit>(Closure.java:197)
At this point I am stuck, as I dont know enough about GraalVM to debug this.
It is also possible that this library cannot actually work with Spring Boot & GraalVM, as Groovy does not seem to be supported as per https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-with-GraalVM#general
+1 I'm facing the exact same issue
Thanks for raising this and for putting together a demo project for me to test with - I'm also new to GraalVM and mostly learned what I know while debugging that last issue that you linked to! 😅 (I was surprised that I could even get something to go given that Groovy is very much in the 'not supported' list of GraalVM.)
I'll give this a look when I get some time and hopefully your demo project reveals something new that I've missed 🤞
Had a look at this over the last few weekends and managed to get something going, and once I pared it all back to see what actually made a difference the solution came back to that java.beans.Introspector was unintentionally initialized at build time.
error that was in the initial post.
I had this error while working on the linked issue as well, and was able to get around it by telling GraalVM to initialize those classes at build time anyway: https://github.com/ultraq/thymeleaf-layout-dialect/blob/1af5824e33f61923a7f5700f2c41ede885f35b62/thymeleaf-layout-dialect-benchmark/source/META-INF/native-image/nz.net.ultraq.thymeleaf/thymeleaf-layout-dialect-benchmark/native-image.properties
So translating those to your setup, I made the following modifications to your pom.xml
file:
Configure the native-maven-plugin
:
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
<buildArgs>
<arg>--initialize-at-build-time=com.sun.beans</arg>
<arg>--initialize-at-build-time=java.beans.Introspector</arg>
</buildArgs>
</configuration>
</plugin>
And I also had to add <arg>-jvm-target=17</arg>
to the <configuration>
/<args>
section of the kotlin-maven-plugin
to get it to compile on my machine, but it looks like your setup didn't need it? 🤷♂️
I'm now wondering if I should include those args in the layout dialect bundle that gets uploaded (I do include a bunch of other stuff which I'm still on the fence about: https://github.com/ultraq/thymeleaf-layout-dialect/blob/1af5824e33f61923a7f5700f2c41ede885f35b62/thymeleaf-layout-dialect/source/META-INF/native-image/nz.net.ultraq.thymeleaf/thymeleaf-layout-dialect/native-image.properties The line with where the responsibility for all this lies is getting very fuzzy and feels like something that should be fixed once GraalVM provides proper Groovy support 🤔
Somehow I missed your update @ultraq
I’ll definitely revisit this on my side too