NullPointerException in MaterialDesignIconFactory because of module encapsulation
When using the latest version of materialdesignfont in a modular JavaFX application on Java 11, a NullPointerException is thrown when using:
MaterialDesignIconFactory.get().createIconButton()
The problem here is that de.jensd.fx.glyphs.GlyphsFactory
is in the module de.jensd.fx.fontawesomefx.commons
, but wants to access the resource /de/jensd/fx/glyphs/materialdesignicons/materialdesignicons-webfont.ttf
in the module de.jensd.fx.fontawesomefx.materialdesignicons
.
But since Java 9 the behaviour of getting resources has changed so that:
A resource in a named module may be encapsulated so that it cannot be located by code in other modules. Whether a resource can be located or not is determined as follows:
- If the resource name ends with "
.class
" then it is not encapsulated. - A package name is derived from the resource name. If the package name is a package in the module then the resource can only be located by the caller of this method when the package is open to at least the caller's module. If the resource is not in a package in the module then the resource is not encapsulated.
In the above, the package name for a resource is derived from the subsequence of characters that precedes the last '/'
in the name and then replacing each '/'
character in the subsequence with '.'
. A leading slash is ignored when deriving the package name. As an example, the package name derived for a resource named "a/b/c/foo.properties
" is "a.b.c
". A resource name with the name "META-INF/MANIFEST.MF
" is never encapsulated because "META-INF
" is not a legal package name.
(copied from https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Module.html#getResourceAsStream(java.lang.String))
Attached to this issue is a minimal gradle example project which shows the behaviour. When executing ./gradlew run
, it produces the following output:
Exception in Application start method
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Exception in Application start method
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.RuntimeException: java.lang.NullPointerException
at de.jensd.fx.fontawesomefx.commons/de.jensd.fx.glyphs.GlyphsFactory.loadFont(GlyphsFactory.java:64)
at de.jensd.fx.fontawesomefx.commons/de.jensd.fx.glyphs.GlyphsFactory.<init>(GlyphsFactory.java:38)
at de.jensd.fx.fontawesomefx.materialdesignicons/de.jensd.fx.glyphs.materialdesignicons.utils.MaterialDesignIconFactory.<init>(MaterialDesignIconFactory.java:28)
at de.jensd.fx.fontawesomefx.materialdesignicons/de.jensd.fx.glyphs.materialdesignicons.utils.MaterialDesignIconFactory.get(MaterialDesignIconFactory.java:33)
at HelloFX.main/foo.bar.HelloApp.start(HelloApp.java:23)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
... 1 more
Caused by: java.lang.NullPointerException
at de.jensd.fx.fontawesomefx.commons/de.jensd.fx.glyphs.GlyphsFactory.loadFont(GlyphsFactory.java:62)
... 13 more
Exception running application foo.bar.HelloApp
FAILURE: Build failed with an exception.
I’m not sure what the best way to fix this properly would be. But i think the easiest way should be to move materialdesignicons-webfont.ttf
out of the package structure to some place which is not regarded as a proper package. Then the resource should be available to other modules (de.jensd.fx.fontawesomefx.commons
in this case).
Comments (2)
-
-
The solution is to change the offending line
Font.loadFont(GlyphsFactory.class.getResource(pathToIconFont).openStream(), 10.0D);
toFont.loadFont(GlyphsFactory.class.getResourceAsStream(pathToIconFont), 10.0D);
How to go about patching the library is another issue.
See more: https://stackoverflow.com/q/54021754 and https://stackoverflow.com/q/65374578
- Log in to comment
The problem is https://bitbucket.org/Jerady/fontawesomefx/src/6cbf97484669c4ab79318a158e1dadf59363c85c/fontawesomefx/fontawesomefx-commons/src/main/java/de/jensd/fx/glyphs/GlyphsFactory.java#lines-62.
GlyphsFactory.class.getResource(pathToIconFont)
returns “null”. Not sure why this happens.I am on Mac OS X. Maybe this has something to do with it.
I am getting this while trying to use PreferencesFX library.