I'm trying to call an external command and print its output in a TextArea
while it is produced. I read the doc about concurrency in JavaFX and I believe I did what I had to do to make it work.
I'm using the Task
class to run my job :
public synchronized void run(ProcessBuilder processBuilder) throws IOException, InterruptedException {
ExternalCommandRunner self = this;
Thread taskThread = new Thread(new Task<Void>() {
@Override
public Void call() throws Exception {
setActive();
try {
runningProcess = processBuilder.start();
StreamPrinter inputStream = new StreamPrinter(runningProcess.getInputStream(), self::handleLog);
StreamPrinter errorStream = new StreamPrinter(runningProcess.getErrorStream(), self::handleLog);
outputTextArea.clear();
new Thread(inputStream).start();
new Thread(errorStream).start();
runningProcess.waitFor();
return null;
} finally {
stop.fire();
setInactive();
}
}
});
taskThread.setDaemon(true);
taskThread.start();
taskThread.join();
}
private void handleLog (String line) { Platform.runLater(() -> outputTextArea.appendText(line + "\n")); }
private void setActive () { setState(STOP_ACTIVE_ICON , false) ; }
private void setInactive() { setState(STOP_INACTIVE_ICON, true) ; }
private void setState (String iconPath, boolean disableButton) {
stop.setGraphic(imageViewFromResource(iconPath, Resources.class));
stop.setDisable(disableButton);
}
However, I'm getting the following exception :
Exception in thread "Thread-5" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-5
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Unknown Source)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(Unknown Source)
at javafx.scene.Parent$2.onProposedChange(Unknown Source)
at com.sun.javafx.collections.VetoableListDecorator.setAll(Unknown Source)
at com.sun.javafx.collections.VetoableListDecorator.setAll(Unknown Source)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(Unknown Source)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(Unknown Source)
at com.sun.javafx.scene.control.skin.ButtonSkin.handleControlPropertyChanged(Unknown Source)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$61(Unknown Source)
at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(Unknown Source)
at javafx.beans.value.WeakChangeListener.changed(Unknown Source)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source)
at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source)
at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source)
at javafx.beans.property.ObjectPropertyBase.set(Unknown Source)
at javafx.css.StyleableObjectProperty.set(Unknown Source)
at javafx.beans.property.ObjectProperty.setValue(Unknown Source)
at javafx.scene.control.Labeled.setGraphic(Unknown Source)
at com.dici.javafx.components.ExternalCommandRunner.setState(ExternalCommandRunner.java:66)
at com.dici.javafx.components.ExternalCommandRunner.setActive(ExternalCommandRunner.java:63)
at com.dici.javafx.components.ExternalCommandRunner.access$000(ExternalCommandRunner.java:19)
at com.dici.javafx.components.ExternalCommandRunner$1.call(ExternalCommandRunner.java:39)
at com.dici.javafx.components.ExternalCommandRunner$1.call(ExternalCommandRunner.java:36)
at javafx.concurrent.Task$TaskCallable.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
stop
is simply a Button
. What am I not doing right ? From the doc, using a Task
this way should be enough...