substrate icon indicating copy to clipboard operation
substrate copied to clipboard

Field com.sun.glass.ui.MenuBar.menus is not present

Open mipastgt opened this issue 6 years ago • 16 comments

When trying to build a native image with mvn clean client:compile I get the following error:

Error: Field com.sun.glass.ui.MenuBar.menus is not present on type java.lang.Object. Error encountered while analysing com.sun.glass.ui.MenuBar.getMenus() 
Parsing context:
	parsing com.sun.javafx.tk.quantum.GlassSystemMenu.setMenus(GlassSystemMenu.java:93)
	parsing com.sun.javafx.tk.quantum.GlassSystemMenu.createMenuBar(GlassSystemMenu.java:73)
	parsing com.sun.javafx.tk.quantum.WindowStage.init(WindowStage.java:146)
	parsing com.sun.javafx.tk.quantum.QuantumToolkit.createTKStage(QuantumToolkit.java:606)
	parsing javafx.stage.Stage.doVisibleChanging(Stage.java:1154)
	parsing javafx.stage.Stage$1.doVisibleChanging(Stage.java:182)
	parsing com.sun.javafx.stage.StageHelper.visibleChangingImpl(StageHelper.java:57)
	parsing com.sun.javafx.stage.WindowHelper.visibleChanging(WindowHelper.java:73)
	parsing javafx.stage.Window$12.invalidated(Window.java:1064)
	parsing javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:110)
	parsing javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:145)
	parsing javafx.stage.Window.setShowing(Window.java:1174)
	parsing javafx.stage.Window.show(Window.java:1189)
	parsing javafx.stage.Stage.show(Stage.java:273)
        ...

According of the sources of my Java version this field should exist. What can be done here?

Added on request: macOS 10.14.6 GraalVM 19.3 client-maven-plugin 0.1.5

mipastgt avatar Nov 22 '19 17:11 mipastgt

First, you can read about how to deal with reflection or reflection in JNI while using GraalVM and native-image.

Then, this is what Gluon Substrate does to solve this issues with native-image: it adds all these json files, with many of the JavaFX classes and their methods and fields that are being used in reflection operations.

However, to make the native compilation phase as short as possible, and the final image size as small as possible, we don't have all the possible classes with all their possible methods.

So an error like you mention happens when there is a reflective call of a class, method or field that is not included in those files.

Solutions:

  • Ideally, that class/method/field should be part of the json files of Gluon Substrate, so filing an issue will help, and it can be included in the next release, (like in this PR).

  • If you don't want to wait, you can do it yourself, cloning and building Substrate.

  • But the most straightforward solution is just to add the class name (in this case com.sun.glass.ui.MenuBar) to the reflectionList or to the jniList, as documented here. All the classes added to these lists will open all the constructors, methods and fields to reflection calls.

jperedadnr avatar Nov 22 '19 17:11 jperedadnr

Thanks for the pointers but adding the class to any of these lists inside the configuration block does not have any significant effect. The error message remains the same.

               <reflectionList>
                    <list>com.sun.glass.ui.MenuBar</list>                        
                </reflectionList>

I'd say there is still another problem but who knows :-( I give up for tonight.

mipastgt avatar Nov 22 '19 18:11 mipastgt

Possible explanation: there is one or more missing classes that when called via reflection fail silently and you don't get a stack trace to find them.

Here is another option you can try: Use the native-image agent to try to discover missing classes.

For that modify the JavaFX maven plugin in your pom with:

<plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.3</version>
                <configuration>
                    <executable>/path/to/graalvm-ce-java11-19.3.0/Contents/Home/bin/java</executable>
                    <mainClass>${mainClassName}</mainClass>
                    <options>
                        <option>-agentlib:native-image-agent=no-filter,config-output-dir=agent</option>
                    </options>
                </configuration>
            </plugin>

save and run mvn clean javafx:run, that will run your application with GraalVM but on hotspot. Try to perform the necessary actions (open menus, click buttons, ...), so all the possible reflection calls are discovered. Close it.

An agent folder will be created in your project with 4 json files. Open the jni and reflection ones, and see if you find classes related to MenuBar. You can compare them with those files in Substrate I linked above. If you see differences, add them (just the class names) to the reflectionList or jniList, and try again.

In any case, please include in your issue(s) your OS, Java SDK version, GraalVM version and plugin version, and if possible, a way to reproduce the issue.

jperedadnr avatar Nov 22 '19 20:11 jperedadnr

Many thanks for the extensive feedback but I already tried all that without success. Actually I started with using the tracing agent a long time ago. Thats why I created https://github.com/gluonhq/client-gradle-plugin/issues/25 and I still think that the only way to go is a direct integration of the tracing agent output into the build process. Manually picking classes from this list in a try and error manner and placing them into the client plugin configuration simply does not work for large projects. (All 4 lists from the agent together are more than 10000 lines long for my app. Just to give you a number.)

mipastgt avatar Dec 01 '19 18:12 mipastgt

Yes, we are already considering that option (agent integration).

In the meantime we have a small "core" of reflection/jni config files, that should cover the initial cases, and for more complex ones we have reflectionList and jniList. But keep in mind that the process is still under heavy development, and of course it is not ready for production!

Back to your issue, what version of JavaFX are you using? What version of Java (and vendor)?

Do you have the JavaFX static build under ~/.gluon/substrate/javafxStaticSdk?

Does it happen in a simple HelloFX project with Menus? (I couldn't reproduce it)

jperedadnr avatar Dec 01 '19 18:12 jperedadnr

Back to your issue, what version of JavaFX are you using? What version of Java (and vendor)?

JavaFX is version 14-ea+2 The agent run was done on GraalVM 19.3(11) The native-image build was done with AdoptOpenJDK (build 11.0.5+10)

Do you have the JavaFX static build under ~/.gluon/substrate/javafxStaticSdk?

Yes.

Does it happen in a simple HelloFX project with Menus? (I couldn't reproduce it)

I haven't tried that yet but if I do the opposite (remove the menu creation from my code) then the error disappears. The problem may be related to the fact that I use NSMenuFX for the macOS integration of the JavaFX menus. I'll try it with a little example but I will not be able to do that before the JFXDays in Zürich starting tomorrow. Will I see you there?

mipastgt avatar Dec 01 '19 19:12 mipastgt

Hmm, NSMenuFX uses private API and it was created for JDK 8. I can barely make it work on Catalina (after adding several add-exports, add-opens), runs but only shows up if I hide the app and show it again?

But it breaks my native build! I don't even get a message error.

Yes, I'll be there on Tuesday, see you!

jperedadnr avatar Dec 01 '19 20:12 jperedadnr

Hmm, NSMenuFX works nicely for me, but I have to admit that I am a bit biased because I once provided the Java 9 adaptation :-) It's true that it does use private API and you need some --add-??? magic but it is still the only way to get a decent menu integration for the mac with JavaFX.

See you on Tuesday.

mipastgt avatar Dec 01 '19 20:12 mipastgt

Hi, I have now put together I simple example which shows the problem. (See attachement.)

This variant of the HelloFX demo uses NSMenuFX to properly customize the system menu on a Mac. To prove that this is actually working the default locale of this demo is explicitly set to GERMAN, so that we can see whether the default system menu items of the Mac are properly internationalized.

The name of the application name in the menu cannot be set programmatically. It defaults to "java" and must be set when a proper app-folder is generated for the executable.

This demo works when run with a JDK but fails when I try to do a native build.

I also tried an example with a standard menu without NSMenuFX and it worked without problems.

HelloFX2.zip

mipastgt avatar Dec 09 '19 13:12 mipastgt

Having a look at your sample, I can reproduce the issue now, thanks.

The stacktrace led to https://github.com/codecentric/NSMenuFX/blob/master/src/main/java/de/codecentric/centerdevice/glass/TKSystemMenuAdapter.java#L32, where MethodHandles are used.

The problem might be related to this known limitation: https://github.com/oracle/graal/blob/master/substratevm/LIMITATIONS.md#invokedynamic-bytecode-and-method-handles

I haven't tried it, but an option could be removing the method handles, and use simple reflection. Then add the reflected classes to the config files.

jperedadnr avatar Dec 22 '19 19:12 jperedadnr

Many thanks for the analysis José. I'll try to find some time over the hollidays and have a look whether NSMenuFX can be modified in this respect.

mipastgt avatar Dec 23 '19 07:12 mipastgt

@jperedadnr Your assumption was right. I have now rewritten the part of NSMenuFX which uses MethodHandles and replaced this code with regular reflection. This works perfectly on a VM and as a result of this I can now build the example code without problem.

But I am still not able to run it because substrate just doesn't support the system menu at all. The call com.sun.javafx.tk.Toolkit.getToolkit().getSystemMenu().isSupported() returns false and without any support for a system menu NSMenuFX also does not work. Even with a standard JavaFX menu, you cannot set the menu bar as a system menu bar.

So, as far as I can see, I cannot procede until substrate supports system menus. I have attached the modified version of NSMenuFX in case you want to try it.

centerdevice-nsmenufx-2.1.6.zip

mipastgt avatar Dec 29 '19 14:12 mipastgt

I have created an issue and a pull-request to modify NSMenuFX accordingly. Issue: https://github.com/codecentric/NSMenuFX/issues/31 Pull: https://github.com/codecentric/NSMenuFX/pull/32

mipastgt avatar Dec 29 '19 16:12 mipastgt

@mipastgt good to know that works!

As for the System Menu, that is a different issue.

As you mentioned, using native-image, getSystemMenu().isSupported() returns false, because there is a check done in GlassApplication.m that returns true only if the app is not running yet.

However, this is not the case when running with Substrate, where the AppDelegate initializes the app first, so when the check in GlassApplication is done, the app is already running. Therefore, isEmbedded = true, and support system menu returns false.

We'll need to investigate the proper fix here.

jperedadnr avatar Dec 29 '19 19:12 jperedadnr

Just an update. My changes to https://github.com/codecentric/NSMenuFX have been merged and are now available in version 2.1.7 via Maven/Gradle. So, NSMenuFX is ready once substrate supports the system menu.

mipastgt avatar Jan 17 '20 10:01 mipastgt

JBS issue: https://bugs.openjdk.java.net/browse/JDK-8279463

jperedadnr avatar Jan 04 '22 19:01 jperedadnr