Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX applications throw NullPointerExceptions but run anyway

Every JavaFX application I've run throws two NullPointerExceptions. They don't prevent or even affect the execution of the projects, and I can only see them if I run my applications in debug mode. I'm even having this issue with the HelloWorld sample from Oracle and this minimal program:

public class JavaFXTSample extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {

        StackPane iAmRoot = new StackPane();

        Scene scene = new Scene(iAmRoot, 300, 250);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main (String[] args) {
        launch(args);
    }
}

Here is the stack trace of the first error:

 Thread [main] (Suspended (exception NullPointerException)) 
    SystemProperties.setVersions() line: 81 [local variables unavailable]   
    SystemProperties.lambda$static$28() line: 67    
    30621981.run() line: not available  
    AccessController.doPrivileged(PrivilegedAction<T>) line: not available [native method]  
    SystemProperties.<clinit>() line: 64    
    LauncherImpl.startToolkit() line: 668   
    LauncherImpl.launchApplicationWithArgs(String, String, String[]) line: 337  
    LauncherImpl.launchApplication(String, String, String[]) line: 328  
    NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]  
    NativeMethodAccessorImpl.invoke(Object, Object[]) line: not available   
    DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: not available   
    Method.invoke(Object, Object...) line: not available    
    LauncherHelper$FXHelper.main(String...) line: not available 

And here is the second:

Thread [JavaFX Application Thread] (Suspended (exception NullPointerException)) 
    PropertyHelper.lambda$getBooleanProperty$514(String) line: 39   
    7164036.run() line: not available   
    AccessController.doPrivileged(PrivilegedAction<T>) line: not available [native method]  
    PropertyHelper.getBooleanProperty(String) line: 37  
    Parent.<clinit>() line: 87  
    JavaFXTSample.start(Stage) line: 16 
    LauncherImpl.lambda$launchApplication1$162(AtomicBoolean, Application) line: 863    
    2266602.run() line: not available   
    PlatformImpl.lambda$runAndWait$175(Runnable, CountDownLatch) line: 326  
    32251660.run() line: not available  
    PlatformImpl.lambda$null$173(Runnable) line: 295    
    11305869.run() line: not available  
    AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]    
    PlatformImpl.lambda$runLater$174(Runnable, AccessControlContext) line: 294  
    30052382.run() line: not available  
    InvokeLaterDispatcher$Future.run() line: 95 
    WinApplication._runLoop(Runnable) line: not available [native method]   
    WinApplication.lambda$null$148(int, Runnable) line: 191 
    32126786.run() line: not available  
    Thread.run() line: not available    

What's more, if I remove any instance of iAmRoot and scene (so start() just reads primaryStage.show();), the second error doesn't occur. Why is this happening?

I've been able to find this question before (JavaFX application throws NullPointerException at startup), but noone seems to have resolved it, and it was asked over 2 years ago.

If it helps, I'm running Eclipse 4.5.2 on Windows 7 Professional, and I don't think I'm using FXML at all.

EDIT:

for what it's worth, I can't find the source code for the second error, but I found JavaFX's code for the method which throws the first error (line 81):

58  private static final String versionResourceName =
59       "/com/sun/javafx/runtime/resources/version.properties";

...

78 private static void setVersions() {
79     int size;
80     InputStream is =
81         SystemProperties.class.getResourceAsStream(versionResourceName);
82     try  {
83         size = is.available();
84         
85         byte[] b = new byte[size];
86         int n = is.read(b);            
87         String inStr = new String(b, "utf-8");
88         SystemProperties.setFXProperty("javafx.version",
89             getValue(inStr, "release="));
90 
91         SystemProperties.setFXProperty("javafx.runtime.version",
92             getValue(inStr, "full="));
93 
94      } catch (Exception ignore) {
95      }
96 }
like image 315
Lord Farquaad Avatar asked Jun 21 '17 19:06

Lord Farquaad


1 Answers

Disclaimer

This question is 1 year old, but i feel like still very relevant. That's why i answer this question (in length).

I'll try to answer this question to the best of my knowledge

TL;DR

JavaFX has some quirky code-lines and the exceptions are cause by those. All can be traced back to some line in JavaFX, which misses a check or uses an unchecked part.

Every JavaFX application I've run throws two NullPointerExceptions. They don't prevent or even affect the execution of the projects, and I can only see them if I run my applications in debug mode.

First NullPointerException

The first Exception you shown is located at:

SystemProperties.setVersions() line: 81 [local variables unavailable]

You already posted the correlating code. To visualize this, i'll post this again. The Line in Problem is:

InputStream is =
            SystemProperties.class.getResourceAsStream(versionResourceName);
try  {
    size = is.available(); // InputStream "is" = null
    ...
} catch (Exception ignore) {
}

This is pretty easy explained. The getResourceAsStream method returns the following (from the JavaDoc):

Returns: A InputStream object or null if no resource with this name is found

This means, that the resource (that you again already posted) "/com/sun/javafx/runtime/resources/version.properties" is not found.

This however is not handled, but simply ignored. This exception is therefor not printed, but still is thrown. It is catched by the debugger, but that's it. Therefor only identifiable by the debugger.

The why is not really well identifiable. One possibility might be that the JDK does not contain the resource. In my tests of the JDK-10 (which i currently use), the package com.sun.javafx.runtime exists, but the sub-package resources does not. This is not really reproducible, since the com.sun package appears to not be documented. The base api of javafx is documented here, but the com.sun.javafx package is not. You can look at it, if you are using a sophisticated IDE (like IntelliJ for example). My JDK-8 does contains it. I use the Oracle JDK on Ubuntu 18.04.1.

One possibility might be, that the package has been ditched along the way. Maybe someone thought, that referencing the resource through packages is not that great and put it within the resources path, but since the exception is swallowed into nothing, this error has never been detected.

Fixing this issue

NOTE: This potential fix is NOT tested thoroughly

This issue might be fixed, if you manually introduce the resource. Considering the code you posted (and that can be found within the SystemProperties.setVersions method), you would have to create a file called "version.properties" within the package com.sun.javafx.runtime.resources. This versions.properties should look like something like this:

release=1
full=1

Those are arbitrary values which you would have to test.

Second NullPointerException

The second NullPointer is rooted within the line PropertyHelper.lambda$getBooleanProperty$514(String) line: 39. This method looks like this:

static boolean getBooleanProperty(final String propName) {
    try {
        boolean answer = AccessController.doPrivileged((java.security.PrivilegedAction<Boolean>) () -> {
                    String propVal = System.getProperty(propName);
                    return "true".equals(propVal.toLowerCase()); // Line 39
                });
        return answer;
    } catch (Exception any) {
    }
    return false;
}

This leads to the conclusion, that the propVal is null, which is backed by the JavaDoc from System.getProperty

Returns: the string value of the system property, or null if there is no property with that key.

This means, the propName is not found. But propName is the method argument. So, what is the propName? This again can be found within the stacktrace. The line:

Parent.() line: 87

defines the property that should be found. It reads the following:

private static final boolean warnOnAutoMove = PropertyHelper.getBooleanProperty("javafx.sg.warn");

So, the method stated before tries to find the SystemProperty "javafx.sg.warn", but does not check whether or not it exists. This again is not handled and therefor only visible within the debugger.

This Exception would not occur if the "toLowerCase" call would be erased.

Fixing this Exception

You can simply fix this exception, by introducing the following code before referencing the Parent class in any way:

System.setProperty("javafx.sg.warn", "true");

It has to be done before any reference, because this attribute is static. So, if this class is referenced in any way in code, it is loaded. One possibility would be, to add a static block to your main class, which contains this code. Another way would be to add the following to the command line:

-Djavafx.sg.warn="true"

This would erase the need to call the code before references. Of course you can exchange true for anything else. But with that, the property exists and the problem line will not throw a NullPointerException.

Why is the second Exception not thrown if only Stage.show is executed?

This is pretty simple. In JavaFx any element to display is a Node within the Scene graph. Every Node may have a Parent, which is a subclass of Node. Nearly all classes extend Parent, so does the StackPane, but not the Stage. By referencing the StackPane, you reference Parent, which triggers the loading of the property. If you do not set a StackPane, you do not reference the Parent class, which means you do not trigger the loading of the property. So no Exception is thrown.

Should you fix the Exceptions?

Since the team behind javafx appears to ignore those exception, you can too. I do not say that this is good practice or you should code in this way, but fixing those issues, that do not prevent you from running this application and that do not change the behavior may cost more time than it is worth.

Further thoughts

This answer explained the raw "why", but i want to give my two cents to the real issue. Please note that this is just my opinion! If you do think differently, that's totally okay. It is no longer directly necessary to answer the question.

This root of all of this is (in my opinion) the many many code smells within the javafx api. From magic strings within all parts, over downcasting within all ParentHelper.ParentAccessor implementations within the components of JavaFX, passing by many Large-Classes (maybe even god classes) and finally ending in Inappropriate intimacy. The abstraction could have been much better and the size of (for example) the Node class is way to enormous.

With the new release cycle, my hopes are high that they take their time to eliminate at least some of those code smells, but i fear that they just build upon those code smells, which means that at some time, a new ("modern") GUI-API has to be build.

Finally

Have a nice day!

like image 83
Thorben Kuck Avatar answered Oct 27 '22 17:10

Thorben Kuck