GraalVM: ClassNotFoundException: com.googlecode.lanterna.gui2.WindowShadowRenderer
Opened this issue · 9 comments
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.
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
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...
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.
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
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.