Javafx - Running Task from initialize method stops immediately - javafx

I am some junior in Java and after didn't find answers in other posts, so decided asking for help.
Target: Need to run inside initialize() method in JavaFx application with below Controller code:
package myAlgo;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.concurrent.Task;
public class Controller{
#FXML
String connectionstatus="";
#FXML
private Connection connection;
#FXML
private TextField connectionStatusTextField;
#FXML
Task<Void> task;
#FXML
public void initialize() {
connection = new Connection();
checkConectionStatus();
}
#FXML
public void checkConectionStatus() {
task = new Task<Void>() {
#Override protected Void call() throws Exception {
while(!isCancelled()) {
connectionstatus=String.valueOf(connection.checkConnection());
connectionStatusTextField.setText(connectionstatus);
Thread.sleep(3000);
}
return null;
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
}
As result the Task inside inialize method is starting running, but stops immediately.
After debuging got java.lang.NullPointerException from FutureTask.class in run() method on row of connection.checkConnection() class at row of "return" in:
public boolean checkConnection() {
return client.isConnected();
}
P.S.
If running it from anouther method - the Task is running without problem.
Tried running after encapsulating by service from (javafx.concurrent.Service), but got the same result.

Related

Update label from nested function called from Task API in JavaFX

I am performing some background task using this class
class Download extends Task{
protected Object call() throws Exception {
try {
updateMessage("Establishing Connection");
DownloadHelper downloadHelper = new DownloadHelper();
downloadHelper.performTask();
return null;
} catch (IOException | ParseException ex) {
logger.error(ExceptionUtils.getStackTrace(ex));
throw ex;
}
}
}
This Task in turn calls DownloadHelper to perform some task.
class DownloadHelper{
public DownloadHelper(){
}
public void performTask(){
----
----
}
}
Is there a way to update the status message of the Task API (updateMessage()) from the DownloadHelper class.?
The expedient approach is to pass a reference to the Download task as a parameter to the DownloadHelper constructor. To minimize coupling, you can instead pass a reference to your implementation of updateMessage() as a parameter of type Consumer, "an operation that accepts a single input argument and returns no result."
DownloadHelper helper = new DownloadHelper(this::updateMessage);
Your helper's implementation of performTask() can then ask the updater to accept() messages as needed.
Consumer<String> updater;
public DownloadHelper(Consumer<String> updater) {
this.updater = updater;
}
public void performTask() {
updater.accept("Helper message");
}
A related example is seen here.
import java.util.function.Consumer;
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
* #see https://stackoverflow.com/q/45708923/230513
*/
public class MessageTest extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("MessageTest");
StackPane root = new StackPane();
Label label = new Label();
root.getChildren().add(label);
Scene scene = new Scene(root, 320, 120);
primaryStage.setScene(scene);
primaryStage.show();
Download task = new Download();
task.messageProperty().addListener((Observable o) -> {
label.setText(task.getMessage());
});
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
private static class Download extends Task<String> {
#Override
protected String call() throws Exception {
updateMessage("Establishing connection");
DownloadHelper helper = new DownloadHelper(this::updateMessage);
helper.performTask();
return "MessageTest";
}
#Override
protected void updateMessage(String message) {
super.updateMessage(message);
}
}
private static class DownloadHelper {
Consumer<String> updater;
public DownloadHelper(Consumer<String> updater) {
this.updater = updater;
}
public void performTask() {
updater.accept("Helper message");
}
}
public static void main(String[] args) {
launch(args);
}
}

Simple example for 'ScheduledService' in Javafx

I am a student and learning JavaFX since a month.
I am developing a application where I want a service to repeatedly start again after its execution of the task. For this I have come to know that 'ScheduledService' is used.
So can anybody please explain the use of scheduledservice with simple example and also how it differs from the 'Service' in JavaFX. Thanks ;)
EDIT : How can I define that this ScheduledService named DataThread should be restarted every 5 seconds ?
public class DataThread extends ScheduledService<Void>
{
#Override
public Task<Void> createTask() {
return new Task<Void>() {
#Override
public Void call() throws Exception {
for(i=0;i<10;i++)
{
System.out.println(""+i);
}
return null;
}
};
}
}
Considering you have a sound knowledge of Service class. ScheduledService is just a Service with a Scheduling functionality.
From the docs
The ScheduledService is a Service which will automatically restart itself after a successful execution, and under some conditions will restart even in case of failure
So we can say it as,
Service -> Execute One Task
ScheduledService -> Execute Same Task at regular intervals
A very simple example of Scheduled Service is the TimerService, which counts the number of times the Service Task has been called. It is scheduled to call it every 1 second
import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TimerServiceApp extends Application {
#Override
public void start(Stage stage) throws Exception {
TimerService service = new TimerService();
AtomicInteger count = new AtomicInteger(0);
service.setCount(count.get());
service.setPeriod(Duration.seconds(1));
service.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
System.out.println("Called : " + t.getSource().getValue()
+ " time(s)");
count.set((int) t.getSource().getValue());
}
});
service.start();
}
public static void main(String[] args) {
launch();
}
private static class TimerService extends ScheduledService<Integer> {
private IntegerProperty count = new SimpleIntegerProperty();
public final void setCount(Integer value) {
count.set(value);
}
public final Integer getCount() {
return count.get();
}
public final IntegerProperty countProperty() {
return count;
}
protected Task<Integer> createTask() {
return new Task<Integer>() {
protected Integer call() {
//Adds 1 to the count
count.set(getCount() + 1);
return getCount();
}
};
}
}
}

How to update text box in JavaFX Application after 5 seconds of running?

I have a single controller class "FXController.java" and my FXApplication.java that extends "Application" and contains the launch code. In a separate class "TestFX.java" I call the start method in the "FXApplication.java" that starts the gui. I want to be able to access its controller so that I can change the text within a textfield of the controller. In my FXApplication.java, within the "launch" method I create a variable for the FXLoader and use the "getController" method and set it to a public variable: public FXController theController.
Within the TestFX.java, after I call the "start" method in the main method that launches FXApplication.java in a new runnable, I try to access the controller to change the contents of a single textfield, I get an exception that says controller is null. What is the proper way for me to change the contents of the textfield? I feel that the threading is causing problems.
What I am trying to do in my main method is:
Launch the JavaFX Application/GUI
5 seconds later (sleep), change the text of the text field in FXController.java to "Hello World".
Note that the fxml file loaded/used by FXApplication.java is pointed correctly to the FXController.java. Am wondering if there is some way to access the controller despite having spawned a new runnable for the FX application.
FXApplication.java
public class FXApplication extends Application {
public FXController theController;
public void start() {
Application.launch(FXApplication.class, args);
}
#Override
public void start(Stage stage) throws Exception {
FXMLLoader fxmll = new FXMLLoader(getClass().getResource("fxml_example.fxml"));
Parent root = fxmll.load();
theController = fxmll.getController();
stage.setTitle("FXML Welcome");
stage.setScene(new Scene(root, 300, 275));
stage.show();
}
}
My TestFX.java
public class TestFX {
public static FXApplication fxApp = new FXApplication();
public ExecutorService execs;
public Future<?> fut;
TestFX(ExecutorService execs) {
this.execs = execs;
}
public void start() {
fut = execs.submit(new Runnable() {
#Override
public void run() {
fxApp.start();
}
});
}
public static void main(String[] args) {
ExecutorService execs = Executors.newCachedThreadPool();
TestFX testFx = new TestFX(execs);
testFx.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//fxApp.theController.setTextBoxText("Hello Word");
Platform.runLater(() -> fxApp.theController.setTextBoxText("Hello Word"));
}
Stuff that you want to do like this, you should use the Task class. This does all the heavy lifting for you and all you have to do is set up the code you want to run the FXAT when the task completes. Here's an example, I've left out the controller stuff, because it just clutters up the concepts:
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class HelloWorld extends Application {
private static Label label;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
label = new Label();
label.setText("Waiting...");
StackPane root = new StackPane();
root.getChildren().add(label);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
Task<Void> sleeper = new Task<Void>() {
#Override
protected Void call() throws Exception {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
return null;
}
};
sleeper.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
label.setText("Hello World");
}
});
new Thread(sleeper).start();
}
}
The "sleeper" Task doesn't do anything except sleep, but it's going to sleep on a new thread so the FXAT can keep on responding to screen activity. Then when the sleep finishes, the event handler for the succeed will run on the thread that instantiated the Task, in this case the FXAT.
Your code has two problems.
First, you are calling the static FXMLLoader.load(URL) method, instead of calling load on your FXMLLoader instance. Consequently, the FXMLLoader instance never gets to initialize its controller. You need
FXMLLoader fxmll = new FXMLLoader(getClass().getResource("fxml_example.fxml"));
Parent root = fxmll.load();
The second issue is that you are then changing the text of the text box from a background thread, instead of from the FX Application Thread. (Unless you're handling this in the controller class: you don't show the code for that.) You need
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Platform.runLater(() -> fxApp.theController.setTextBoxText("Hello Word"));
You can also do this with a PauseTransition:
PauseTransition pause = new PauseTransition(Duration.seconds(5));
pause.setOnFinished(event -> fxApp.theController.setTextBoxText("Hello Word"));
pause.play();

Javafx service to update GUI objects status

I'm trying to update the GUI status based on a time consuming task. When I push on a button, I want the button to be inactive and the cursor to change until the job is completed. I've come up with this code that mostly works as needed.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class TestWait2 extends Application {
#Override
public void start(Stage primaryStage) {
FlowPane root = new FlowPane();
primaryStage.setScene(new Scene(root));
MyService myService = new MyService();
primaryStage.getScene().getRoot().cursorProperty()
.bind(Bindings.when(myService.runningProperty())
.then(Cursor.WAIT).otherwise(Cursor.DEFAULT));
Button startButton = new Button();
startButton.setText("Button");
startButton.disableProperty().bind(myService.runningProperty());
root.getChildren().add(startButton);
startButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
myService.start();
}
});
primaryStage.show();
}
private class MyService extends Service<Void> {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
Thread.sleep(5000);
return null;
}
};
}
}
public static void main(String[] args) {
launch(args);
}
}
When I launch it works great the first time. The problem is that if I click on the button a second time it get an error.
Exception in thread "JavaFX Application Thread" java.lang.IllegalStateException: Can only start a Service in the READY state. Was in state SUCCEEDED
Any thoughts on how to get around that issue?
I'm running on Java 8u5.
The problem with your code that you try to call myService.start(); when its on SUCCEEDED state(since you started it already once).
this is according to javadoc of Service start method
Starts this Service. The Service must be in the READY state to succeed in * *this call.
This method should only be called on the FX application thread.
to make your code work, you need to you call myService.restart().
since you are planning to use your service over and over you can do the following:
startButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
//replace this line
// myService.start();
//with this
myService.restart();
}
});
Adding this to the program seemed to solve this.
myService.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
myService.reset();
}
});
I also found I can add the following directly to the MyService class.
#Override
protected void succeeded() {
reset();
}
There doesn't seem to be any other way to have it go to the ready state after completing it's work.

Using a JavaFx application instance from another class

I have a MainWindowFx class like below. It basically creates a simple JavaFX GUI.
package drawappfx;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.control.TextAreaBuilder;
/**
*
* #author Hieu
*/
public class MainWindowFX extends Application{
public static final int DEFAULT_WIDTH = 600;
public static final int DEFAULT_HEIGHT = 600;
private int width;
private int height;
private Scene scene;
private TextArea messageView;
private Button quitButton;
private BorderPane layout;
private Stage primaryStage;
#Override
public void start(Stage primaryStage) {
System.out.println("Started building GUI....");
this.buildGUI();
System.out.println("Finished building GUI");
this.primaryStage = primaryStage;
primaryStage.setTitle("Hello World!");
primaryStage.setScene(this.scene);
primaryStage.show();
System.out.println("Where the hell are you?");
}
public Scene getScene() {
return this.scene;
}
public BorderPane getBorderPane() {
return this.layout;
}
public Stage getPrimaryStage() {
return this.primaryStage;
}
public void buildGUI() {
System.out.println("Before layout");
this.layout = new BorderPane();
System.out.println("Before vbox");
this.layout.setBottom(this.addVBox());
System.out.println("before new scene");
this.scene = new Scene(this.layout, DEFAULT_WIDTH, DEFAULT_HEIGHT);
System.out.println("after new scene");
}
public VBox addVBox() {
VBox vbox = new VBox();
vbox.setPadding(new Insets(15, 12, 15, 12));
// message box
this.messageView = TextAreaBuilder.create()
.prefRowCount(5)
.editable(false)
.build();
// quit button
this.quitButton = new Button("Quit");
this.quitButton.setPrefSize(100, 20);
System.out.println("think of a good message?");
vbox.getChildren().addAll(this.messageView, this.quitButton);
System.out.println("before returning vbox");
return vbox;
}
public void postMessage(final String s) {
this.messageView.appendText(s);
}
}
Now I want to use an instance of this object in another class:
package drawappfx;
import java.io.InputStreamReader;
import java.io.Reader;
import javafx.scene.layout.BorderPane;
public class DrawAppFx
{
public static void main(String[] args)
{
final MainWindowFX main = new MainWindowFX();
BorderPane layout = main.getBorderPane();
Reader reader = new InputStreamReader(System.in);
Parser parser = new Parser(reader,layout,main);
main.start(main.getPrimaryStage());
parser.parse();
}
}
But when I run this I ran into this error:
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.javafx.main.Main.launchApp(Main.java:658)
at com.javafx.main.Main.main(Main.java:805)
Caused by: java.lang.IllegalStateException: Not on FX application thread; currentThread = main
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:237)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:397)
at javafx.scene.Scene.<init>(Scene.java:287)
at javafx.scene.Scene.<init>(Scene.java:226)
at drawappfx.MainWindowFX.buildGUI(MainWindowFX.java:74)
at drawappfx.MainWindowFX.start(MainWindowFX.java:47)
at drawappfx.DrawAppFx.main(DrawAppFx.java:39)
... 6 more
Java Result: 1
I've done some searches on this and guessed that it has something to do with threading... but I still have no idea. Any suggestions?
I've had this problem several times and there is a fairly easy way to resolve it.
First of all let me introduce you to the Mediator pattern, basically you want to create a class that has the relationship with all your GUI classes
(I.e the different GUI classes do not have their own instance of each other instead all of them has the same reference to the Mediator).
That was a sidetrack now to your question.
In order to change window you need to pass the Stage of which the new window should be placed upon because of this your code needs only a minor change:
Now I do not often do this but in your case, I will make an exception the following code consists of a class that you can "Copy Paste" into your program and use that will fix the problem after the code I will explain exactly what I did:
Mediator
public class Mediator extends Application {
private DrawAppFx daf;
private MainWindowFX mainWindow;
private Stage primaryStage;
public Mediator(){
daf = new DrawAppFx(this);
mainWindow = new MainWindowFx(this);
}
public static void main(String[] args){
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
primaryStage = stage;
mainWindow.start(primaryStage);
}
public void changeToDaf(){
daf.start(primaryStage);
}
}
Now each of the DrawAppFx and MainWindowFx must have a constructor that passes a Mediator object and therefore have a "Has-a" relationship with the mediator
The reason behind this is that the mediator pattern is now in control and should you create more windows it is easy to implement just add them to the mediator and add a method for which the mediator can change to that window.

Resources