17

I have a simple console application which sometimes need to perform graphics operations, for those I'm using JavaFx framework (there are some functions that I need like the css styling for text ) I simply generate some shapes and text into an hidden scene then save those on file and that's all,

I know that to work with JavaFx I have to pass graphics operations to the JavaFx thread, but when everything is done and I have to close the application (after some hours) this JavaFx thread still remain open... and I really don't want to force exit with System.exit() because if something is blocked I may want to know/wait (ALSO I don't want to execute everything as an JavaFx application (as JavaFx components are less than 1% of my main application)

the code is very simple and googling around I've found only to use

Platform.exit();

which doesn't seems to work, I've also tried playing with Platform parameters like

Platform.setImplicitExit(false);

here is my test application which you can run :

import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.geometry.Pos;
import javafx.scene.layout.VBox;

public class SOTestFX {
    public static void main(String[] args) {
        SOTestFX t = new SOTestFX();
        t.runFxThread();
    }

    public  void runFxThread(){
        //Application.launch(args);
        final JFXPanel jfxPanel = new JFXPanel(); 
        Platform.runLater(new Runnable() {
            @Override
            public void run() {

                System.err.println("CREATING IMAGE");
                simpleFXoperations();
                System.err.println("NOW CALL EXIT");
                System.err.println("JAVA FX THREAD SHOULD BE EXITED NOW");
                Platform.exit();
            }
        });

        try {
            Thread.sleep(3000); // just wait a bit if something should happen, let it happen..
        } catch (InterruptedException e) {
            e.printStackTrace();  
        }
        //jfxPanel.removeNotify(); // return ->  java.lang.NullPointerException
        //Platform.exit(); // -> does nothing

        System.err.println("i will never die!");
    }
    public void simpleFXoperations(){

        VBox vbox1 = new VBox();
        vbox1.setAlignment(Pos.BOTTOM_CENTER);
        vbox1.setStyle("-fx-border-style: solid;"
                + "-fx-border-width: 1;"
                + "-fx-border-color: white");
        System.err.println("simpleFXoperations() _DONE");
    }
}

and this is the thread which never close

"Attach Listener" - Thread t@17 java.lang.Thread.State: RUNNABLE

Locked ownable synchronizers: - None

"JavaFX Application Thread" - Thread t@13 java.lang.Thread.State: RUNNABLE at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method) at com.sun.glass.ui.gtk.GtkApplication$3$1.run(GtkApplication.java:82) at java.lang.Thread.run(Thread.java:722)

Locked ownable synchronizers: - None

Update: I'm using latest Oracle JDK 7u17 64bit on Linux Fedora 16 64bit.

artless noise
  • 21,212
  • 6
  • 68
  • 105
Francesco
  • 317
  • 1
  • 4
  • 13
  • I don't have any answer for this and I don't know why `Platform.exit` does not work for your test case. Try filing an issue at https://javafx-jira.kenai.com – jewelsea Apr 04 '13 at 18:40
  • thank you, but does it works for you ? do you have a similar test case in which it works? on all my tests seems that Platform.exit() simply doesn't work.. :( – Francesco Apr 05 '13 at 09:25

4 Answers4

24

Fix:

I was able to fix this problem by calling com.sun.javafx.application.PlatformImpl.tkExit() immediately before Platform.exit(). I don't really understand the JavaFX source that well, but it seems to be working; YMMV.

Update: Doing this in Java 8 will produce a warning, you can just turn the warning off with @SuppressWarnings("restriction"). It shouldn't be a problem.

Explanation:

I figured this out by digging through the source code; JFXPanel has this little snippet (this is from JavaFX 2.2.25)

finishListener = new PlatformImpl.FinishListener() {
  public void idle(boolean paramAnonymousBoolean) {
    if (!JFXPanel.firstPanelShown) {
      return;
    }
    PlatformImpl.removeListener(JFXPanel.finishListener);
    JFXPanel.access$102(null);
    if (paramAnonymousBoolean)
      Platform.exit();
  }

  public void exitCalled()
  {
  }

The problem is, if you are using only a little bit of JavaFX in your application, then the idle(boolean) method never does anything (because firstPanelShown == false), which prevents the listener from getting removed, which prevents the JavaFX Toolkit from shutting down... which means you have to shut it down manually.

Community
  • 1
  • 1
durron597
  • 31,968
  • 17
  • 99
  • 158
  • That makes sense, it won't help when the application is deployed. I've already seen JavaFX application terminate but it is still running actively in the background via the NetBean task-status. While it is running (or active). You can't delete the JAR or re-start the application without a reboot! – will Jun 27 '14 at 10:52
  • Relying on `com.sun` methods is not something I would want to do. Especially not considering OpenJDK, and/or Java 9. – Simon Forsberg Dec 24 '15 at 00:21
  • I have read [this](http://meta.stackoverflow.com/a/253066/3375713) ranting meta answer of yours and thought your hard work should not go uncredited. So, I am doing all I can: +1 :-) – Sнаđошƒаӽ Feb 03 '16 at 09:51
  • Throws `java.lang.IllegalStateException: Toolkit has exited` – shinzou Sep 13 '16 at 19:19
  • @kuhaku it's entirely possible this answer is obsolete, that can happen when you make internal, undocumented API calls :-/ – durron597 Sep 13 '16 at 22:22
2

Your main function does not belong to the JavaFx Application object and i think that your program never eneter application thread loop.

It seems you should do:

public class SOTestFX extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    @Override
    public void start(Stage stage) throws Exception {
        // Do stuff here to show your stage or whatever you want;
        // This will be called on the JavaFX thread
    }
}
LeonidVlad
  • 83
  • 8
  • 1
    but I don't want to enclose the whole application (which is more than 40k rows of code ) just because a small class (of less than 800 rows) which I need to manipulate some images need JavaFX, the application is not even graphics it's a console application, thank you anyway – Francesco Apr 12 '13 at 14:29
  • Well it seems it is indeed the problem, look at the source of LauncherImpl which is called in lauch method of Application: https://bitbucket.org/openjfxmirrors/openjfx-8-graphics-rt/src/1444a737ab40db93f4fee4f13d7122858ef5fad4/javafx-ui-common/src/com/sun/javafx/application/LauncherImpl.java?at=default – zenbeni Nov 06 '13 at 15:44
2

That's a slippery situation, as (to my understanding) the purpose of the JavaFX thread is to take advantage of various hardware pipelines transparently. Might I suggest placing your JavaFX requests in a separate, referenced, project; and keep everything else, including your main method, in another? That's always worked for me.

Basically, business logic and model go in one project, and view and control (generally JavaFX-based) go in the other. This allows for independent termination of the JavaFX thread. Hopefully that is applicable to what you are trying to do.

Michael Oberlin
  • 479
  • 4
  • 4
  • Yes, agree about separation. but what do you do when you want the user of a console app to select a file, for example? Surely you're going to allow them to use FileChooser, with no Stage? PS I currently find that Platform.exit() works fine to shut down the JavaFX thread. – mike rodent Oct 28 '16 at 08:05
1

I tried a lot of things on this as none of the above answers worked for me. Best thing for me was to just shutdown the whole JVM using

System.exit(1);

as my whole Application is just that one JavaFX application and therefor can be shut down, when closing the Stage. The 1 in my case is just a random int.

Wingie
  • 111
  • 1
  • 2
  • 10