Question

I've made a Java/JavaFX console, and now I face with an exception: Console reports an Internal error.The error is: java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-5. The code of the Console:

package core.console;
import javafx.concurrent.Service;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;

import java.io.*;
import java.util.ResourceBundle;

public class Console implements Runnable{
    private Console(ResourceBundle resourceBundle) throws IOException {
        FXMLLoader loader = new FXMLLoader(this.getClass().getResource("Console.fxml"), resourceBundle);
        Parent root = (Parent) loader.load();
        controller = loader.getController();
        Scene scene = new Scene(root);
        stage = new Stage();
        stage.setScene(scene);
        textArea = controller.getTextArea();
        show();

        PipedOutputStream pout=new PipedOutputStream(this.pin);
        System.setOut(new PrintStream(pout,true));

        PipedOutputStream pout2=new PipedOutputStream(this.pin2);
        System.setErr(new PrintStream(pout2,true));

        System.setIn(new PipedInputStream(this.pout3));

        reader = new Thread(this);
        reader.setDaemon(true);
        reader.start();

        reader2 = new Thread(this);
        reader2.setDaemon(true);
        reader2.start();
    }   

    public static Console getInstance(ResourceBundle resourceBundle) {
        if (console == null) {
            try {
                console = new Console(resourceBundle);
            } catch (IOException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        }
        return console;
    }

    public void show() {
        stage.show();
    }

    @Override
    public synchronized void run()
    {
        try
        {
            while (Thread.currentThread()==reader)
            {
                try {
                    this.wait(100);
                } catch(InterruptedException ie) {}
                if (pin.available()!= 0)
                {
                    String input=this.readLine(pin);
                    controller.appendText(input);
                }
                if (quit) return;
            }

            while (Thread.currentThread()==reader2)
            {
                try {
                    this.wait(100);
                } catch(InterruptedException ie) {}
                if (pin2.available()!= 0)
                {
                    String input = this.readLine(pin2);
                    controller.appendText(input);
                }
                if (quit) return;
            }
        } catch (Exception e)
        {
            controller.appendText("\nConsole reports an Internal error.");
            controller.appendText("The error is: "+e);
        }

    }

    private synchronized String readLine(PipedInputStream in) throws IOException
    {
        String input="";
        do
        {
            int available=in.available();
            if (available==0) break;
            byte b[]=new byte[available];
            in.read(b);
            input=input+new String(b,0,b.length);
        }while( !input.endsWith("\n") &&  !input.endsWith("\r\n") && !quit);
        return input;
    }   

    private static Console console = null;
    private ConsoleController controller;
    private Stage stage;
    private TextArea textArea;
    private Thread reader;
    private Thread reader2;

    private final PipedInputStream pin=new PipedInputStream();
    private final PipedInputStream pin2=new PipedInputStream();
    private final PipedOutputStream pout3=new PipedOutputStream();


}

When starting the application, the console gives me described above exception, but everthing works. But if an exception is generated by the application, the console doesn't show it and everything became locked. What did I do wrong?

Was it helpful?

Solution

JavaFX is a single-threaded toolkit. You should never query or update the UI from the background threads. So you need to wrap all the calls to the JFX classes with

Platform.runLater(new Runnable() {
   @Override
   public void run() {
      // Update/Query the FX classes here
   }
});

In your context, the code in the Console.run() method is executed off the JavaFX application thread, so it should not be directly modifying UI objects by invoking controller.appendText(). In your case all of the controller.appendText() calls should be wrapped in the Platform.runLater construct defined above.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top