vaadin / flow

Vaadin Flow is a Java framework binding Vaadin web components to Java. This is part of Vaadin 10+.
Apache License 2.0
589 stars 164 forks source link

Missing native-image hints for PWA icon #19123

Open javier-godoy opened 2 months ago

javier-godoy commented 2 months ago

Description of the bug

Running a Vaadin 24.3.7 application with @PWA and a custom icon (located at src/main/resources/META-INF/resources/icons/icon.png), compiled into a native image using GraalVM 21 results in the following errors. Interestingly, the Tracing Agent did not provide the anticipated hints during initial testing.

java.lang.NoClassDefFoundError: Could not initialize class java.awt.GraphicsEnvironment$LocalGE
        at java.desktop@21.0.2/java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(GraphicsEnvironment.java:106) ~[addons-demos-24-native.exe:na]
        at java.desktop@21.0.2/java.awt.image.BufferedImage.createGraphics(BufferedImage.java:1182) ~[addons-demos-24-native.exe:na]
        at com.vaadin.flow.server.PwaIcon.drawIconImage(PwaIcon.java:268) ~[na:na]
java.lang.NoSuchMethodError: sun.awt.windows.WToolkit.windowsSettingChange()V
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.functions.JNIFunctions$Support.getMethodID(JNIFunctions.java:1341) ~[na:na]
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.functions.JNIFunctions$Support.getMethodID(JNIFunctions.java:1326) ~[na:na]
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.functions.JNIFunctions.GetMethodID(JNIFunctions.java:431) ~[na:na]
        at java.desktop@21.0.2/sun.awt.windows.WToolkit.initIDs(Native Method) ~[na:na]
        at java.desktop@21.0.2/sun.awt.windows.WToolkit.<clinit>(WToolkit.java:190) ~[na:na]
        at java.desktop@21.0.2/sun.awt.Win32GraphicsEnvironment.<clinit>(Win32GraphicsEnvironment.java:60) ~[na:na]
        at java.desktop@21.0.2/sun.awt.PlatformGraphicsInfo.createGE(PlatformGraphicsInfo.java:34) ~[na:na]
        at java.desktop@21.0.2/java.awt.GraphicsEnvironment$LocalGE.createGE(GraphicsEnvironment.java:93) ~[na:na]
        at java.desktop@21.0.2/java.awt.GraphicsEnvironment$LocalGE.<clinit>(GraphicsEnvironment.java:84) ~[na:na]
        at java.desktop@21.0.2/java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(GraphicsEnvironment.java:106) ~[addons-demos-24-native.exe:na]
        at java.desktop@21.0.2/java.awt.image.BufferedImage.createGraphics(BufferedImage.java:1182) ~[addons-demos-24-native.exe:na]
        at com.vaadin.flow.server.PwaIcon.drawIconImage(PwaIcon.java:268) ~[na:na]

#16307 also mentions the following hint, which in my case was produced by the Tracing Agent:

{
  "name":"sun.java2d.Disposer",
  "methods":[{"name":"addRecord","parameterTypes":["java.lang.Object","long","long"] }]
}

Expected behavior

In 2022 @mstahv said that "Vaadin don’t yet come with these hints", but now there is VaadinHintsRegistrar and the documentation does not mention a word about hints, so I'm inclined to think that I could expect it to work.

Icon generation in native apps seems to be a known issue, though https://github.com/vaadin/flow/issues/16307#issuecomment-1602241827.

Minimal reproducible example

A Vaadin 24.3.7 application with @PWA and a custom icon.

Versions

mcollovati commented 2 months ago

Looking at Quarkus AwtProcessor it seems that there have been some changes in GraalVM 23 related to AWT. For example this block of code https://github.com/quarkusio/quarkus/blob/4b8a27a5d591841d579a1b8ea1041b5fa481e8cf/extensions/awt/deployment/src/main/java/io/quarkus/awt/deployment/AwtProcessor.java#L276-L287

that points to https://github.com/oracle/graal/issues/4921.

Link to the Quarkus commit https://github.com/quarkusio/quarkus/commit/a3546217ba0de66dea6d1744e22434edaee24fe1

javier-godoy commented 2 months ago

In my case it was a Spring Boot application. I'm fine with maintaining the hints locally, but maybe they should be added to the framework.

mcollovati commented 2 months ago

I mentioned the Quarkus class only because the hints in Flow were updated based on it

javier-godoy commented 2 months ago

I was able to reproduce NoSuchMethodError: sun.awt.windows.WToolkit.windowsSettingChange()V by playing a sound in the browser:

//https://gist.github.com/literallylara/7ece1983fab47365108c47119afb51c7
//(C) Lara Sophie Schütt 2016, CC0 
for(var i=44100*0.1,d="";i--;)d+=String.fromCharCode(~~((Math.sin(i/44100*2*Math.PI*800)+1)*128)); 
bellSound = "data:Audio/WAV;base64,"+btoa("RIFFdataWAVEfmt "+atob("EAAAAAEAAQBErAAARKwAAAEACABkYXRh/////w==")+d);
new Audio(bellSound).play();

Collect Metadata with the Tracing Agent

  1. Start the application with Tracing Agent, open application in browser, terminate. jni-config before playing sound

  2. Start the application with Tracing Agent, open application in browser, open developer tools, execute script above, terminate. jni-config after playing sound

(Browser requests icons/icon-180x180.png when playing a sound)

Required hints

The required hints (in jni-config-after, but neither in jni-config-before, nor provided by VaadinBeanFactoryInitializationAotProcessor) are:

[
{
  "name":"java.awt.Component"
},
{
  "name":"java.awt.Font",
  "fields":[{"name":"name"}, {"name":"pData"}, {"name":"size"}, {"name":"style"}],
  "methods":[{"name":"getFont","parameterTypes":["java.lang.String"] }, {"name":"getFontPeer","parameterTypes":[] }]
},
{
  "name":"java.awt.desktop.UserSessionEvent$Reason",
  "fields":[{"name":"CONSOLE"}, {"name":"LOCK"}, {"name":"REMOTE"}, {"name":"UNSPECIFIED"}]
},
{
  "name":"sun.awt.AWTAutoShutdown",
  "methods":[{"name":"notifyToolkitThreadBusy","parameterTypes":[] }, {"name":"notifyToolkitThreadFree","parameterTypes":[] }]
},
{
  "name":"sun.awt.SunToolkit",
  "methods":[{"name":"isTouchKeyboardAutoShowEnabled","parameterTypes":[] }]
},
{
  "name":"sun.awt.Win32GraphicsEnvironment",
  "methods":[{"name":"dwmCompositionChanged","parameterTypes":["boolean"] }]
},
{
  "name":"sun.awt.image.SunVolatileImage",
  "fields":[{"name":"volSurfaceManager"}]
},
{
  "name":"sun.awt.image.VolatileSurfaceManager",
  "fields":[{"name":"sdCurrent"}]
},
{
  "name":"sun.awt.windows.WDesktopPeer",
  "methods":[{"name":"systemSleepCallback","parameterTypes":["boolean"] }, {"name":"userSessionCallback","parameterTypes":["boolean","java.awt.desktop.UserSessionEvent$Reason"] }]
},
{
  "name":"sun.awt.windows.WToolkit",
  "methods":[{"name":"displayChanged","parameterTypes":[] }, {"name":"windowsSettingChange","parameterTypes":[] }]
},
{
  "name":"sun.java2d.windows.WindowsFlags",
  "fields":[{"name":"d3dEnabled"}, {"name":"d3dSet"}, {"name":"offscreenSharingEnabled"}, {"name":"setHighDPIAware"}]
}
]

After adding these hints, I was able to play the sound in the native image, with no exceptions being logged when the icon is requested. Note that one of the hints is about sun.awt.Win32GraphicsEnvironment, so it's platform-specific.

So far, I've not been able to reproduce NoClassDefFoundError for java.awt.GraphicsEnvironment$LocalGE

Versions

Vaadin / Flow version: 24.3.7 Java version: GraalVM 22+36.1 OS version: Windows 10.0.19045.4170 Browser: Chrome 123.0.6312.88