2

I'm working on a JavaFX project in Netbeans that's currently around 3000 lines long, and I've packaged as an .exe regularly for testing but never come across an issue like this curious incident.

When packaging natively as a Windows .exe file, I found that after installing the .exe and launching my program I was getting a popup saying "Class {mypackage}/{mymainclass} not found." followed by "Failed to launch JVM."

Launching the program in Netbeans, packaging as an .exe, and launching the .jar in Powershell with "java -jar {app}.jar" all gave absolutely no errors. Even the .jar inside the installed application folder was fine, with no errors on the command line.

After a few hours of trawling through git commits and packaging natively, I managed to trace the issue down to a single line of code: private static ComboBox choices = new ComboBox();

When I initialise the ComboBox in an initialiser, the program magically works after being installed from an .exe:

private static ComboBox choices;
{
    choices = new ComboBox();
}

However, when I use a static initialiser (by placing static in front of the first curly brace), even though Netbeans states "initialiser can be static", I get the same error as before.

This is puzzling, because Java is obviously fine with the code itself, but after it's been through the native packager it will not launch. I've used plenty of similar lines of code to initialise static variables in other classes, with no ill effects.

I tried adding a similar line to the main class: private static CheckBox chkbox = new CheckBox();

It caused the exact same error after the .exe was installed (as before, the .jar was fine), but when I cut-and-pasted the line to a different class, it had no effect.

Interestingly, my main class already has a static boolean that is initialised in the same way that the ComboBox and CheckBox were: private static boolean someBool = true; but the boolean causes no problems.

Can anyone explain the reason behind this? Thank you.

Edit: I also tried packaging on a different machine, but it stopped working after the same git commit. I'm using version 1.8.0_144 of the JDK and JRE.

Edit 2: here is the main class of a simple example that produces the error.

package testproject;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class MainApplication extends Application {

    private static CheckBox testCheckBox = new CheckBox(); // Causes error after launching from installed .exe

    /* UNCOMMENT THIS SECTION AND REMOVE THE " = new CheckBox()" ABOVE TO FIX THE ERROR
    {
        testCheckBox = new CheckBox();
    }
     */

    @Override
    public void start(Stage primaryStage) {
        StackPane root = new StackPane();
        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Empty window");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

}
TheGreatCabbage
  • 155
  • 1
  • 2
  • 8
  • 2
    Can you provide some MCVE? https://stackoverflow.com/help/mcve Would help to have this reproducable, maybe there is something different wrong too (like the JDK-version you used). – FibreFoX Sep 07 '17 at 06:46
  • Thanks for your reply. I have succeeded in producing a simple project which shows the same error, here's the link to the BitBucket repo: https://bitbucket.org/TheGreatCabbage/javafx-test-project/src/21c2dd82b2b07f2ecb9c9d775a406fd37d7e818b?at=master The only class is in src/testproject. – TheGreatCabbage Sep 07 '17 at 10:45
  • please post the code in your questions ... linked references tend to get stale over time – kleopatra Sep 07 '17 at 13:41
  • Could be serialization. Deserialization circumvents using constructors, and doing initialisation. That seems to be akin. – Joop Eggen Sep 07 '17 at 13:42
  • I've posted the code. Would serialization explain how the .jar works fine, but the .exe does not? – TheGreatCabbage Sep 07 '17 at 13:48
  • I suspect a threading issue (creating `CheckBox` in static initializer means it happens in main thread, unlike with instance init that is running in JavaFX thread). You could try removing `main` method, you should still be able to launch your app with `java` launcher, because JavaFX does not need that, but that might break exe packaging... or fix the bug :) How do you package to `.exe` ? – Hugues M. Sep 07 '17 at 14:26
  • Removing the main method causes no issues with Java but it seems to break the packaging process, because I get the same error as before when launching the .exe (even with the fixed version of the code). I'm packaging using Netbeans' "Package as -> EXE Installer" option, which uses Inno Setup 5. – TheGreatCabbage Sep 07 '17 at 14:33

1 Answers1

3

I would avoid creating JavaFX widgets in static initializers (such as a static field or static block), as this would create them when class is loaded, which might happen in any thread. You want that to happen in JavaFX Application Thread.
In your case it happens in main thread, which is definitely not correct.

Check this answer for more details about JavaFX application startup, and how threads are involved in the process.

Trying to initialize a JavaFX widget outside of "JavaFX Application Thread" is (to my understanding) undefined behavior, and might fail in many different ways (or succeed by luck). If that creation fails, then the class loading fails (because static init has failed), hence "class not found". Unfortunately, since this happens while loading main class, you don't get to see the initial error, just "class XXX not found".

Possibly in your case, the java launcher can deal with this initialization just fine, but the launcher used by your .exe is different. I'm not too surprised it fails. I'm equally surprised it works with regular java :)

Hugues M.
  • 19,846
  • 6
  • 37
  • 65