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

GraalVM native-image fails with minimum configuration #235

Open
shainegordon opened this issue Apr 9, 2023 · 4 comments
Open

GraalVM native-image fails with minimum configuration #235

shainegordon opened this issue Apr 9, 2023 · 4 comments
Assignees
Labels

Comments

@shainegordon
Copy link

shainegordon commented Apr 9, 2023

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

@dmengelt
Copy link

dmengelt commented Apr 9, 2023

+1 I'm facing the exact same issue

@ultraq
Copy link
Owner

ultraq commented Apr 10, 2023

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 🤞

@ultraq ultraq self-assigned this Apr 10, 2023
@ultraq
Copy link
Owner

ultraq commented Apr 22, 2023

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 🤔

@shainegordon
Copy link
Author

Somehow I missed your update @ultraq

I’ll definitely revisit this on my side too

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

No branches or pull requests

3 participants