Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8175977

SplashScreen preventing Platform.exit() from exiting in JavaFX app

XMLWordPrintable

      FULL PRODUCT VERSION :
      java version "1.8.0_121"
      Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
      Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Darwin myLab.local 16.4.0 Darwin Kernel Version 16.4.0: Thu Dec 22 22:53:21 PST 2016; root:xnu-3789.41.3~3/RELEASE_X86_64 x86_64

      A DESCRIPTION OF THE PROBLEM :
      If a JavaFX application is launched with parameter -splash:path_to_image, Platform.exit() won't be able to exit the application in a clean way and the System menubar is OSX will stop to be supported.


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1 - Create a hello world in JavaFX with a menu called "File".
      2 - Be sure to have menuBar.setUseSystemMenuBar(true) so the menu will display in MacOS menuBar and Platform.exit() in your exit() or stop() method and add it a QuitHandler.
      3 - Run it using java -cp ... , app here will run normally , exit normally and "File" menu will be displayed exactly in the system menu bar.
      4 - Run this time with a splash screen , "java -splash:path_to_img -cp ..."

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      1 - A splash screen is displayed before at startup
      2 - Menu "File" is displayed in the OSX system menu bar.
      3 - When Quit is clicked , application exits without any exception in the log.
      ACTUAL -
      1 - A splash screen is displayed before at startup
      2 - Menu "File" is displayed in the OSX system menu bar.
      3 - When 'Quit' menuItem is clicked , application exits with exceptions.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :

      If application is launched from the command line as described before , it shows the following exception on exit :

      Exception in thread "AppKit Thread" java.lang.IllegalStateException: Not on FX application thread; currentThread = AppKit Thread
      at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:236)
      at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
      at javafx.stage.Window.setShowing(Window.java:921)
      at javafx.stage.Window.hide(Window.java:947)
      at com.sun.javafx.stage.WindowPeerListener.closed(WindowPeerListener.java:100)
      at com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:118)
      at com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:40)
      at java.security.AccessController.doPrivileged(Native Method)
      at com.sun.javafx.tk.quantum.GlassWindowEventHandler.lambda$handleWindowEvent$423(GlassWindowEventHandler.java:150)
      at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
      at com.sun.javafx.tk.quantum.GlassWindowEventHandler.handleWindowEvent(GlassWindowEventHandler.java:148)
      at com.sun.glass.ui.Window.handleWindowEvent(Window.java:1266)
      at com.sun.glass.ui.Window.notifyDestroy(Window.java:1183)


      OR the following exceptions if application is launched using install4j :

       31792 ERROR com.idc.util.javafx.SimpleUncaughtExceptionHandler uncaughtException 34 Uncaught exception
      java.lang.IllegalStateException: Not on FX application thread; currentThread = JavaFX Application Thread
      at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:236) ~[jfxrt.jar:?]
      at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423) ~[jfxrt.jar:?]
      at javafx.stage.Window.setShowing(Window.java:921) ~[jfxrt.jar:?]
      at javafx.stage.Window.hide(Window.java:947) ~[jfxrt.jar:?]
      at com.sun.javafx.stage.WindowPeerListener.closed(WindowPeerListener.java:100) ~[jfxrt.jar:?]
      at com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:118) ~[jfxrt.jar:?]
      at com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:40) ~[jfxrt.jar:?]
      at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_121]
      at com.sun.javafx.tk.quantum.GlassWindowEventHandler.lambda$handleWindowEvent$423(GlassWindowEventHandler.java:150) ~[jfxrt.jar:?]
      at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389) ~[jfxrt.jar:?]
      at com.sun.javafx.tk.quantum.GlassWindowEventHandler.handleWindowEvent(GlassWindowEventHandler.java:148) ~[jfxrt.jar:?]
      at com.sun.glass.ui.Window.handleWindowEvent(Window.java:1266) ~[jfxrt.jar:?]
      at com.sun.glass.ui.Window.notifyDestroy(Window.java:1183) ~[jfxrt.jar:?]
        

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
       ---> HelloWorldApplication.java

      package helloworld;

      import com.apple.eawt.AppEvent;
      import com.apple.eawt.QuitResponse;

      import java.security.AccessControlException;
      import java.util.Optional;

      import javafx.application.Application;
      import javafx.application.Platform;
      import javafx.scene.Scene;
      import javafx.scene.control.Alert;
      import javafx.scene.control.ButtonType;
      import javafx.scene.control.Menu;
      import javafx.scene.control.MenuBar;
      import javafx.scene.control.MenuItem;
      import javafx.scene.control.SeparatorMenuItem;
      import javafx.scene.control.TextArea;
      import javafx.scene.layout.BorderPane;
      import javafx.stage.Stage;

      import org.apache.logging.log4j.LogManager;
      import org.apache.logging.log4j.Logger;

      /**
       * Sample application.
       * @author me
       */
      public class HelloWorldApplication extends Application {
      // constants --------------------------------------------------------------------------------
         private static final Logger LOG = LogManager.getLogger();

         /**
          * Running on Mac platform.
          */
         public static final boolean MAC;

         static {
            boolean mac = false;
            try {
               final String osName = System.getProperty("os.name");
               LOG.debug("OS: {}", osName);
               mac = osName != null && osName.toLowerCase().contains("mac");
            } catch (AccessControlException ex) {
               LOG.debug("Cannot determine OS");
            }
            MAC = mac;
         }

      // member variables -------------------------------------------------------------------------
         private final Alert mAlert = new Alert(Alert.AlertType.CONFIRMATION);

      // methods ----------------------------------------------------------------------------------
         @Override
         public void start(Stage pStage) {
            final BorderPane root = new BorderPane();

            final MenuBar menuBar = new MenuBar();

            final Menu fileMenu = new Menu("_File");
            menuBar.getMenus().add(fileMenu);

            // Exit
            boolean macQuitMenuItem = false;
            if (MAC) {
               final com.apple.eawt.Application application = com.apple.eawt.Application.getApplication();
               try {
                  application.setQuitHandler(
                     (AppEvent.QuitEvent pEvt, QuitResponse pQuitResponse) -> Platform.runLater(
                        () -> exit(pQuitResponse::cancelQuit)
                     )
                  );
                  macQuitMenuItem = true;

                  // occurs when running as untrusted applet
               } catch (AccessControlException ex) {
                  LOG.debug("Cannot listen for application quit");
               }
            }
            if (!macQuitMenuItem) {
               fileMenu.getItems().add(new SeparatorMenuItem());

               final MenuItem exitMenuItem = new MenuItem("E_xit");

               exitMenuItem.setOnAction(pEvt -> exit(() -> {}));
               fileMenu.getItems().add(exitMenuItem);
            }


            root.setTop(menuBar);
            root.setCenter(new TextArea("Hello, world!"));

            final Scene scene = new Scene(root, 300, 250);

            pStage.setTitle("Hello World!");
            pStage.setScene(scene);

            mAlert.initOwner(pStage);
            mAlert.setTitle("Confirm Exit");
            mAlert.setHeaderText("Look, a Confirmation Dialog");
            mAlert.setContentText("Are you sure you want to exit?");

            pStage.show();
         }

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

         public void exit() {exit(() -> {});}

         public void exit(Runnable pAbortExitRunnable) {
            LOG.info("Exit");
            if (checkUnsavedChanges()) {
               Platform.exit();
            } else {
               pAbortExitRunnable.run();
            }
         }

         private boolean checkUnsavedChanges() {
            final Optional<ButtonType> result = mAlert.showAndWait();
            return result.isPresent() && result.get() == ButtonType.OK;
         }
      }


      ---> pom.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>

          <groupId>idc</groupId>
          <artifactId>Bug125</artifactId>
          <version>1.0-SNAPSHOT</version>

          <dependencies>
              <dependency>
                  <groupId>com.yuvimasory</groupId>
                  <artifactId>orange-extensions</artifactId>
                  <version>1.3.0</version>
              </dependency>

              <dependency>
                  <groupId>org.apache.logging.log4j</groupId>
                  <artifactId>log4j-api</artifactId>
                  <version>2.8</version>
              </dependency>
              <dependency>
                  <groupId>org.apache.logging.log4j</groupId>
                  <artifactId>log4j-core</artifactId>
                  <version>2.8</version>
              </dependency>

          </dependencies>

          <build>
              <plugins>
                  <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-dependency-plugin</artifactId>
                      <version>3.0.0</version>
                      <executions>
                          <execution>
                              <id>copy-dependencies</id>
                              <phase>initialize</phase>
                              <goals>
                                  <goal>copy-dependencies</goal>
                              </goals>
                          </execution>
                      </executions>
                  </plugin>
                  <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-compiler-plugin</artifactId>
                      <configuration>
                          <source>1.8</source>
                          <target>1.8</target>
                      </configuration>
                  </plugin>
                  <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-jar-plugin</artifactId>
                      <version>3.0.2</version>
                      <configuration>
                          <archive>
                              <index>true</index>
                              <manifest>
                                  <addClasspath>true</addClasspath>
                                  <mainClass>helloworld.HelloWorldApplication</mainClass>
                              </manifest>
                          </archive>

                      </configuration>

                  </plugin>
              </plugins>
          </build>
      </project>

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Use JavaFX Preloader instead of SplashScreen.

            Unassigned Unassigned
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: