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

Incomplete mechanism for preventing changing JavaFX component from non-FX thread

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: P4 P4
    • tbd
    • jfx11, jfx20, 8, jfx17, jfx19, jfx21
    • javafx
    • x86_64
    • windows_10

      ADDITIONAL SYSTEM INFORMATION :
      Windows 10 21H2, jdk-8u251, IntelliJ 2020.2 Ultimate version as the IDE

      A DESCRIPTION OF THE PROBLEM :
      There are some report (JDK-8095034) (JDK-8298104) points this issue, but there are no simple examples to reproduce this results. I would like to provide an example to show how non-FX thread modify the FX components without triggering the IllegalStateException ("Not on FX application thread").


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Use the example source code with Java 1.8.0_251. (I am using IntelliJ 2020.2 Ultimate version as the IDE)

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The IllegalStateException ("Not on FX application thread") is expected to be triggered (or if this is safe, the program should run without any exceptions)
      ACTUAL -
      Nullpointerexception is triggered after waiting for several seconds:

      Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
      at javafx.scene.Scene$ScenePulseListener.synchronizeSceneNodes(Scene.java:2289)
      at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2419)
      at com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:398)
      at java.security.AccessController.doPrivileged(Native Method)
      at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:397)
      at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:424)
      at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:561)
      at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:541)
      at com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:534)
      at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:340)
      at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
      at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
      at com.sun.glass.ui.win.WinApplication.lambda$null$4(WinApplication.java:186)
      at java.lang.Thread.run(Thread.java:748)


      Or in some cases, ArrayIndexOutofBoundsException is triggered:

      Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException: -1
      at java.util.ArrayList.elementData(ArrayList.java:422)
      at java.util.ArrayList.get(ArrayList.java:435)
      at com.sun.javafx.collections.ObservableListWrapper.get(ObservableListWrapper.java:89)
      at com.sun.javafx.collections.VetoableListDecorator.get(VetoableListDecorator.java:306)
      at javafx.scene.Parent.updateCachedBounds(Parent.java:1591)
      at javafx.scene.Parent.recomputeBounds(Parent.java:1535)
      at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1388)
      at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
      at javafx.scene.Node.updateGeomBounds(Node.java:3577)
      at javafx.scene.Node.getGeomBounds(Node.java:3530)
      at javafx.scene.Node.getLocalBounds(Node.java:3478)
      at javafx.scene.Node.updateTxBounds(Node.java:3641)
      at javafx.scene.Node.getTransformedBounds(Node.java:3424)
      at javafx.scene.Node.updateBounds(Node.java:559)
      at javafx.scene.Parent.updateBounds(Parent.java:1719)
      at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2404)
      at com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:398)
      at java.security.AccessController.doPrivileged(Native Method)
      at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:397)
      at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:424)
      at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:561)
      at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:541)
      at com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:534)
      at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:340)
      at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
      at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
      at com.sun.glass.ui.win.WinApplication.lambda$null$4(WinApplication.java:186)
      at java.lang.Thread.run(Thread.java:748)



      ---------- BEGIN SOURCE ----------
      Main File:

      package sample;

      import javafx.application.Application;
      import javafx.fxml.FXML;
      import javafx.fxml.FXMLLoader;
      import javafx.scene.Parent;
      import javafx.scene.Scene;
      import javafx.stage.Stage;

      public class Main extends Application {
          FXMLLoader fxmlLoader;

          @Override
          public void start(Stage primaryStage) throws Exception{
              fxmlLoader = new FXMLLoader(getClass().getResource("sample.fxml"));
              Parent root = fxmlLoader.load();
              Controller controller = fxmlLoader.getController();
              controller.configureBinding();
              controller.setupTimer();

              primaryStage.setTitle("Hello World");
              primaryStage.setScene(new Scene(root, 300, 275));
              primaryStage.show();
          }


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

      Controller File:
      package sample;

      import javafx.application.Platform;
      import javafx.beans.property.SimpleStringProperty;
      import javafx.fxml.FXML;
      import javafx.scene.control.Label;

      import java.util.Timer;
      import java.util.TimerTask;

      public class Controller {
          @FXML
          private Label label_test;

          @FXML
          private Label label_test1;

          @FXML
          private Label label_test2;

          @FXML
          private Label label_test3;

          @FXML
          private Label label_test4;

          @FXML
          private Label label_test5;

          @FXML
          private Label label_test6;

          @FXML
          private Label label_test7;

          @FXML
          private Label label_test8;

          @FXML
          private Label label_test9;


          private Timer timer;
          private Timer timer2;

          private int test = 0;
          private int test2 = 0;

          private final SimpleStringProperty simpleStringProperty = new SimpleStringProperty();

          public void configureBinding(){
              simpleStringProperty.addListener((observable, oldValue, newValue) -> {
                  if (simpleStringProperty.get().equals("1")){
                      label_test.setVisible(true);
                      label_test.setDisable(false);

                      label_test1.setVisible(true);
                      label_test1.setDisable(false);

                      label_test2.setVisible(true);
                      label_test2.setDisable(false);

                      label_test3.setVisible(true);
                      label_test3.setDisable(false);

                      label_test4.setVisible(true);
                      label_test4.setDisable(false);

                      label_test5.setVisible(true);
                      label_test5.setDisable(false);

                      label_test6.setVisible(true);
                      label_test6.setDisable(false);

                      label_test7.setVisible(true);
                      label_test7.setDisable(false);

                      label_test8.setVisible(true);
                      label_test8.setDisable(false);

                      label_test9.setVisible(true);
                      label_test9.setDisable(false);
                  }else{
                      label_test.setVisible(false);
                      label_test.setDisable(true);

                      label_test1.setVisible(false);
                      label_test1.setDisable(true);

                      label_test2.setVisible(false);
                      label_test2.setDisable(true);

                      label_test3.setVisible(false);
                      label_test3.setDisable(true);

                      label_test4.setVisible(false);
                      label_test4.setDisable(true);

                      label_test5.setVisible(false);
                      label_test5.setDisable(true);

                      label_test6.setVisible(false);
                      label_test6.setDisable(true);

                      label_test7.setVisible(false);
                      label_test7.setDisable(true);

                      label_test8.setVisible(false);
                      label_test8.setDisable(true);

                      label_test9.setVisible(false);
                      label_test9.setDisable(true);
                  }
              });
          }

          public void setupTimer(){
              timer = new Timer();
              timer.schedule(new TimerTask() {
                  @Override
                  public void run() {
                      simpleStringProperty.set(test + "");
                      test = (test + 1) % 2;
                  }
              }, 0, 1);


          }

      }

      FXML file:
      <?xml version="1.0" encoding="UTF-8"?>

      <?import javafx.scene.control.Label?>
      <?import javafx.scene.layout.ColumnConstraints?>
      <?import javafx.scene.layout.GridPane?>
      <?import javafx.scene.layout.RowConstraints?>

      <GridPane alignment="center" hgap="10" vgap="10" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
          <children>
              <Label fx:id="label_test" text="Label" />
            <Label fx:id="label_test1" text="Label" />
            <Label fx:id="label_test2" text="Label" />
            <Label fx:id="label_test3" text="Label" />
            <Label fx:id="label_test4" text="Label" />
            <Label fx:id="label_test5" text="Label" />
            <Label fx:id="label_test6" text="Label" />
            <Label fx:id="label_test7" text="Label" />
            <Label fx:id="label_test8" text="Label" />
            <Label fx:id="label_test9" text="Label" />
          </children>
         <columnConstraints>
            <ColumnConstraints />
         </columnConstraints>
         <rowConstraints>
            <RowConstraints />
         </rowConstraints>
      </GridPane>



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

      CUSTOMER SUBMITTED WORKAROUND :
      Move the code that may modify JavaFX components into Platform.runLater().

      FREQUENCY : occasionally


            kcr Kevin Rushforth
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: