mabe02/lanterna

GraalVM: ClassNotFoundException: com.googlecode.lanterna.gui2.WindowShadowRenderer

Opened this issue · 9 comments

damnms commented

Hi, i have a little application and would love to create native images with graalvm.
Unfortunately, when i run the built native-image, i get:

Exception in thread "main" java.lang.ExceptionInInitializerError
        at com.googlecode.lanterna.gui2.AbstractTextGUI.<init>(AbstractTextGUI.java:60)
        at com.googlecode.lanterna.gui2.MultiWindowTextGUI.<init>(MultiWindowTextGUI.java:169)
        at com.googlecode.lanterna.gui2.MultiWindowTextGUI.<init>(MultiWindowTextGUI.java:76)
        at com.googlecode.lanterna.gui2.MultiWindowTextGUI.<init>(MultiWindowTextGUI.java:65)
        at jsfdl.adapters.CursesWindow.<init>(CursesWindow.java:52)
        at jsfdl.ApplicationConfiguration.getGui(ApplicationConfiguration.java:62)
        at jsfdl.adapters.GuiApplication.main(GuiApplication.java:10)
        at java.base@21.0.1/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: com.googlecode.lanterna.gui2.WindowShadowRenderer
        at com.googlecode.lanterna.graphics.AbstractTheme.instanceByClassName(AbstractTheme.java:154)
        at com.googlecode.lanterna.graphics.PropertyTheme.<init>(PropertyTheme.java:66)
        at com.googlecode.lanterna.bundle.DefaultTheme.<init>(DefaultTheme.java:11)
        at com.googlecode.lanterna.bundle.LanternaThemes.<clinit>(LanternaThemes.java:52)
        ... 8 more
Caused by: java.lang.ClassNotFoundException: com.googlecode.lanterna.gui2.WindowShadowRenderer
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:122)
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:86)
        at java.base@21.0.1/java.lang.Class.forName(DynamicHub.java:1346)
        at java.base@21.0.1/java.lang.Class.forName(DynamicHub.java:1309)
        at java.base@21.0.1/java.lang.Class.forName(DynamicHub.java:1302)
        at com.googlecode.lanterna.graphics.AbstractTheme.instanceByClassName(AbstractTheme.java:152)
        ... 11 more

line 52 of CursesWindow is: final WindowBasedTextGUI textGUI = new MultiWindowTextGUI(screen);

I assume its because of the dynamic loading in AbstractTheme.java:152, wondering if i can somehow manually "add" those classes that are required (for linux/windows/mac) to graalvm, so they are packaged.

avl42 commented
damnms commented

all classes thare are instantiated by reflection are:
com.googlecode.lanterna.gui2.WindowShadowRenderer
com.googlecode.lanterna.gui2.FatWindowDecorationRenderer
com.googlecode.lanterna.gui2.Button$DefaultButtonRenderer

any idea how to use the direct renderer classes? guess thats the easiest workaround/solution

avl42 commented
damnms commented

my build tool is gradle, i dont know how i can "whitelist" some classes. i guess that has to be done in the graalvm native image task.

i tried adding those classes that i need in my code, but that did not work:
i added System.out.println(GuiApplication.pullIn());//TODO: so nasty... :P in my static main, and the method contains:

       private static String pullIn() {
        return com.googlecode.lanterna.gui2.WindowShadowRenderer.class.toString() +
                com.googlecode.lanterna.gui2.FatWindowDecorationRenderer.class.toString() +
                com.googlecode.lanterna.gui2.Button.DefaultButtonRenderer.class.toString();
    }

when i run the compiled image, it says:

oli@fedora:~/IdeaProjects/jsfdlloader/build/native/nativeCompile$ ./jsfdlloader 
class com.googlecode.lanterna.gui2.WindowShadowRendererclass com.googlecode.lanterna.gui2.FatWindowDecorationRendererclass com.googlecode.lanterna.gui2.Button$DefaultButtonRenderer
Exception in thread "main" java.lang.ExceptionInInitializerError
        at com.googlecode.lanterna.gui2.AbstractTextGUI.<init>(AbstractTextGUI.java:60)
        at com.googlecode.lanterna.gui2.MultiWindowTextGUI.<init>(MultiWindowTextGUI.java:169)
        at com.googlecode.lanterna.gui2.MultiWindowTextGUI.<init>(MultiWindowTextGUI.java:76)
        at com.googlecode.lanterna.gui2.MultiWindowTextGUI.<init>(MultiWindowTextGUI.java:65)
        at org.gitlab.jsfdl.adapters.CursesWindow.<init>(CursesWindow.java:52)
        at org.gitlab.jsfdl.ApplicationConfiguration.getGui(ApplicationConfiguration.java:62)
        at org.gitlab.jsfdl.adapters.GuiApplication.main(GuiApplication.java:16)
        at java.base@21.0.1/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: com.googlecode.lanterna.gui2.WindowShadowRenderer
        at com.googlecode.lanterna.graphics.AbstractTheme.instanceByClassName(AbstractTheme.java:154)
        at com.googlecode.lanterna.graphics.PropertyTheme.<init>(PropertyTheme.java:66)
        at com.googlecode.lanterna.bundle.DefaultTheme.<init>(DefaultTheme.java:11)
        at com.googlecode.lanterna.bundle.LanternaThemes.<clinit>(LanternaThemes.java:52)
        ... 8 more
Caused by: java.lang.ClassNotFoundException: com.googlecode.lanterna.gui2.WindowShadowRenderer
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:122)
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:86)
        at java.base@21.0.1/java.lang.Class.forName(DynamicHub.java:1346)
        at java.base@21.0.1/java.lang.Class.forName(DynamicHub.java:1309)
        at java.base@21.0.1/java.lang.Class.forName(DynamicHub.java:1302)
        at com.googlecode.lanterna.graphics.AbstractTheme.instanceByClassName(AbstractTheme.java:152)
        ... 11 more

so i guess something is not working correctly. maybe the compiler removes that by some sort of optimization algorithm, no idea...

damnms commented

to make sure its not optimized away i used real instances and got the following outpu:
oli@fedora:~/IdeaProjects/jsfdlloader/build/native/nativeCompile$ ./jsfdlloader
com.googlecode.lanterna.gui2.WindowShadowRenderer@366774a6com.googlecode.lanterna.gui2.FatWindowDecorationRenderer@747e2db8com.googlecode.lanterna.gui2.Button$DefaultButtonRenderer@25ba116b
then the exceptions... as above

so the classes are there, they can be instantiated. seems like this is something else thats not working.

damnms commented

seems like #521 is the same problem

avl42 commented
damnms commented

gradle does not really produce an executable, its just like maven. it has some addons/plugins that can e.g. create a shadowjar (aka fatjar). and gradle can handle graalvm - which really produces a standalone application.
so the problem is in graalvm's plugin for gradle, not gradle itself. at least for me it looks like that.

oli@fedora:~/IdeaProjects/jsfdlloader/build/native/nativeCompile$ file jsfdlloader 
jsfdlloader: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=c93142e2698f5f940ea91930f8b350512ebecb86, for GNU/Linux 3.2.0, with debug_info, not stripped

its a real executable and yes, graalvm supports reflection: https://www.graalvm.org/22.2/reference-manual/native-image/guides/build-with-reflection/

when i grep that binary or search with vim, i find some of those WindowShadowRenderer, so i am really lost whats going on

damnms commented

i think i got it working...
i was not sure if the tutorial also works with jar files, but it does
https://www.graalvm.org/22.2/reference-manual/native-image/guides/build-with-reflection/

first i created the shadow jar, which produces the mentioned errors above (classnotfound etc.).
so i executed the command like in the tutorial but a bit different.
i went into my $PROJECTROOT/build/libs and created a META-INF/native-image directory, then:
graalvm-jdk-21.0.1+12.1/bin/java -agentlib:native-image-agent=config-output-dir=META-INF/native-image -jar myapplication-all.jar
that produces some files in META-INF/native-image, which i moved then to $PROJECT_ROOT/src/main/resources.
With those, i re-created the shadowJar and checked that those files are inside the .jar under META-INF/native-image.
Then i was able to run graalvm-jdk-21.0.1+12.1/bin/native-image -jar myapplication-all.jar which produces the native-image.
so seems like, its a must have to have those configuration generated by graalvm. not sure if there is another way.