Why is ExecutorService not returning the results of my Task? - javafx

UPDATE:
I have a button on a JavaFx App that should do the login after user input email and password.
<Button fx:id="loginButton" layoutX="157.0" layoutY="254.0" mnemonicParsing="false" onAction="#login" prefHeight="30.0" prefWidth="172.0" text="Login" />
I have an ExecutorService on a JavaFX app. I created a task to search the DB. It executes the query well and gets the UserInfo object, the problem is, the ExecutorService is not passing the results to the main thread. This is the main code that is having a problem of null returned from the executor service:
public class LoginController {
#FXML
private Button loginButton;
#FXML
private Label afterLoginText;
#FXML
private TextField email;
#FXML
private PasswordField password;
#FXML
private Hyperlink hyperlink;
#FXML
private ProgressBar progressBar;
private Navegador navegador;
public void login(ActionEvent event) {
afterLoginText.setText("Login in, please wait...");
String emailText = email.getText();
String passwordText = password.getText();
DAOGeneric<UserInfo> dao = new DAOGeneric<>();
LoginAtDataBaseTask loginAtDataBaseTask = new LoginAtDataBaseTask(dao, emailText, passwordText);
progressBar.progressProperty().bind(loginAtDataBaseTask.progressProperty());
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(loginAtDataBaseTask);
loginAtDataBaseTask.setOnSucceeded(workerStateEvent -> {
UserInfo userInfo;
try {
userInfo = (UserInfo) future.get();
} catch (InterruptedException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (ExecutionException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
if(userInfo == null){
System.out.println("User info is null");
}
else{
try {
changeToMainScreen(event, userInfo);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
//Set premium level if user have one
//Optional - show premium info
}
});
executorService.shutdown();
}
public void changeToMainScreen(ActionEvent event, UserInfo userInfo) throws IOException {
Stage stage = (Stage) ((Node)event.getSource()).getScene().getWindow();
runMainScreen("/mainScreen.fxml",stage, userInfo);
}
And here is the code of the LoginAtDataBaseTask Class:
public class LoginAtDataBaseTask extends Task <UserInfo> {
private static DAOGeneric<UserInfo> dao;
private static String email;
private static String password;
public LoginAtDataBaseTask(DAOGeneric<UserInfo> dao, String email, String password) {
this.dao = dao;
this.email = email;
this.password = password;
}
#Override
protected UserInfo call() {
return doLogin();
}
private UserInfo doLogin(){
final int maxProgress = 100;
List<UserInfo> usersList = dao.findByAnyParameter("email", email, UserInfo.class);
if(usersList.size() == 1){
updateProgress(99,maxProgress);
UserInfo user1 = usersList.get(0);
String passwordDecoded = DecoderMD5.StringToMD5(password);
if(user1.getUserPassword().equals(passwordDecoded)){
// afterLoginText.setText("Login Sucess!");
return user1;
}
else{
// afterLoginText.setText("Wrong Password!");
}
}
else if(usersList.size()>1){
//More than one user with same email on BD
// afterLoginText.setText("Error code 1 - report to administrator");
}
else if(usersList.size()==0){
// afterLoginText.setText("This email is not registered! Please register first!");
}
else{
//Erro at DAO Search
// afterLoginText.setText("Error code 2 - report to administrator");
}
return null;
}
}
I tried casting on many ways and using Future first to receive the submit and then calling get on the future object, but nothing seems to work. I have already readed the java documents of this related classes but i don't really understand why my object keeps null.
UPDATE: I put a setOnSucceeded but the future.get keeps returning null, and the main javafx ui keeps freezing. What am i keeping doing wrong and what can i do to solve it?

Why does get() return null?
This has to do with the fact that Task is, fundamentally, an implementation of Runnable. It is not an implementation of Callable. Therefore, you are calling #submit(Runnable) which returns a Future<?>1, meaning no result is expected. A Runnable cannot return a value. In other words, the call to #get() will always return null in this case.
But you should really be calling #execute(Runnable) when passing a Task to an ExecutorService, anyway. There's no reason to have a Future object representing the status of the Task. This is for at least two reasons:
The call to Future#get() is a blocking call. The whole purpose of Task is to communincate a result specifically back to the JavaFX Application Thread. And you must never block that thread, as doing so will lead to an unresponsive UI.
A Task is a FutureTask2, which means it is already a Future. If you really need to wait for a result (not on the FX thread), then you can just call #get() on the Task instance.
1. It really should have been defined to return Future<Void>
2. Note that the standard ExecutorService implementation, ThreadPoolExecutor, wraps all submitted Runnable and Callable objects in a FutureTask, at least by default.
How to get the result of a Task
As noted earlier, the purpose of Task (and the other javafx.concurrent classes) is to offload work to a background thread but communicate a result (as well as messages, progress, etc.) back to the JavaFX Application Thread. That thread specifically, not any other thread. But you must not block the FX thread. That means observing the task for completion, not waiting for it to complete. Then when it does complete, you react by doing what needs doing.
But how to get the value from a completed Task? You query its value property, which will be set to whatever is returned by the #call() method if and when the task succeeds. You can directly observe this property with a listener, if you want. Personally, I prefer using the onSucceeded and onFailed properties. For example:
Task<SomeObject> task = ...;
task.setOnSucceeded(e -> {
SomeObject result = task.getValue();
// do something with 'result'
});
task.setOnFailed(e -> {
task.getException().printStackTrace(); // or log it with a proper logging framework
// notify user of failure
});
executor.execute(task);
Note properties of Task like message, progress, value and so on are guaranteed to only be set by the FX thread. And the onXXX handlers are also guaranteed to be invoked only by the FX thread.
See Concurrency in JavaFX and javafx.concurrent documentation for more information.

Related

JavaFX Task updateValue throws IllegalStateException: Not on FX application thread

I have a simple application with a single JavaFX window. I'm sending in data to an Azure IoTHub inside a for loop. This for loop is in a JavaFX Task, and the for loop has a small delay (Thread.sleep(300)) so progress can be shown on the UI. I have 2 labels I want to update during the data transmission, always showing the latest sent in data. I have the following helper class for this:
public class DataHelper {
private StringProperty date = new SimpleStringProperty();
private StringProperty count = new SimpleStringProperty();
public DataHelper() {
}
public DataHelper(String date, String count) {
this.date.setValue(date);
this.count.setValue(count);
}
//getters and setters
}
And here is my sendErrorsToHub method inside my UI controller class:
private void sendErrorsToHub(List<TruckErrorForCloud> toCloud) {
DataHelper dataHelper = new DataHelper("", "");
Task task = new Task<DataHelper>() {
#Override
public DataHelper call() {
try {
int i = 0;
for (TruckErrorForCloud error : toCloud) {
Thread.sleep(300);
i++;
String strMessage = Utility.toPrettyJson(null, error);
if (strMessage != null) {
Message msg = new Message(strMessage);
msg.setMessageId(java.util.UUID.randomUUID().toString());
client.sendEventAsync(msg, null, null);
}
updateProgress(i, toCloud.size());
DataHelper dh = new DataHelper(error.getErrorTimeStamp().substring(0, error.getErrorTimeStamp().length() - 9),
String.valueOf(error.getCount()));
updateValue(dh);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void updateValue(DataHelper value) {
super.updateValue(value);
dataHelper.setDate(value.getDate());
dataHelper.setCount(value.getCount());
}
//succeeded method omitted
};
dateValue.textProperty().bind(dataHelper.dateProperty());
countValue.textProperty().bind(dataHelper.countProperty());
progressBar.progressProperty().bind(task.progressProperty());
new Thread(task).start();
}
When I run the application, I constantly get IllegalStateException: Not on FX application threadexceptions, inside the updateValue method. As far as I understand the documentation, the whole point of the updateValue method, that it runs on the Application thread, and it can be used to pass a custom object, which can be used to update the UI.
What am I doing wrong then?
The bottom of the stacktrace with my classes is the following:
at eu.mantis.still_rca_simulator.gui.DataHelper.setDate(DataHelper.java:28)
at eu.mantis.still_rca_simulator.gui.GuiController$1.updateValue(GuiController.java:166)
at eu.mantis.still_rca_simulator.gui.GuiController$1.call(GuiController.java:155)
at eu.mantis.still_rca_simulator.gui.GuiController$1.call(GuiController.java:138)
(138 is the line Task task = new Task(), 155 updateValue(dh);, 166 dataHelper.setDate(value.getDate());)
updateValue does not automatically run on the application thread and it's not necessary to run it on the application thread since it takes care of updating the value property of Task on the application thread.
Your code in the overridden version updateValue executes logic on the background thread that needs to be run on the application thread though:
dataHelper.setDate(value.getDate());
dataHelper.setCount(value.getCount());
The bindings result in the text properties being updated from the background thread since the above code runs on the background thread.
In this case I recommend using a immutable DataHelper class and updating the ui using a listener to the value property:
Remove the updateValue override and the dataHelper local variable, initialize the gui with empty strings, if necessary, declare task as Task<DataHelper> task and do the following to update the gui:
task.valueProperty().addListener((o, oldValue, newValue) -> {
if (newValue != null) {
dateValue.setText(newValue.getDate());
countValue.setText(newValue.getCount());
}
});
You may also use Platform.runLater for those updates, since they don't happen frequently enough to result in issues that could be the result of using Platform.runLater too frequently.

JavaFX skips instructions while connecting to server

I have JavaFX application with two scenes. When user fills nick and clicks on button, it should change scenes and then connect to server. But for some unknown reason it first connects to server and change scenes only when it gets respond from server. But it should first do the two set Visible instructions and only then create the Vysilac or am I wrong ?
Here is my code - Button method ( spusteni and cekani are names of the scenes ), Vysilac is class that handles the connection.
Vysilac vysilac;
#FXML
private void pripojSe() throws IOException
{
String nick = textNick.getText();
if (nick != null && !nick.equals(""))
{
spusteni.setVisible(false);
cekani.setVisible(true);
vysilac = new Vysilac(nick);
}
}
public class Vysilac
{
private String nick = "Chyba - nick neexistuje";
private Socket socket;
private ObjectOutputStream output;
private ObjectInputStream input;
Vysilac( String nick) throws IOException
{
this.nick = nick;
socket = new Socket("localhost",33);
output = new ObjectOutputStream(socket.getOutputStream());
input = new ObjectInputStream(socket.getInputStream());
//System.out.println(nick);
output.writeObject(nick);
}
}
Thank for any help or advices:)
You need to run long-running operations on a seperate thread. Otherwise you block the JavaFX application thread that is responsible for updating the UI. In this case I'd use a Task to get the job done:
spusteni.setVisible(false);
cekani.setVisible(true);
Task<Vysilac> task = new Task<Vysilac>() {
#Override
protected Vysilac call() throws IOException {
return new Vysilac(nick);
}
};
// register state handlers
task.setOnSucceeded(evt -> vysilac = task.getValue());
task.setOnFailed(evt -> {
// TODO: error handling
});
new Thread(task).start();

JavaFX Why The Task returns Null?

I'm trying to learn Threads and Tasks for days now ... but still can't implement it in my app ... help plz.
I want to run all Database operations in a background thread other than the application thread.
I have a class that manages the Database queries ... in this class i surrounded the executeQuery statement with a task:
public class Database {
ResultSet rs;
public ResultSet execQuery(PreparedStatement stmnt) throws SQLException {
Task<ResultSet> task = new Task<ResultSet>() {
#Override protected ResultSet call() throws Exception {
if (isCancelled()) {
}
ResultSet execRs = stmnt.executeQuery();
return execRs;
}
};
task.run();
task.setOnSucceeded(new EventHandler<WorkerStateEvent>(){
#Override
public void handle(WorkerStateEvent event) {
rs = task.getValue();
}
});
return rs;
}
// remaining Code
}
As you can see the method should return a ResultSet but when i call this from another place it raise a null pointer ... the result set returned by this method is null.
So what did i do wrong here?
Update #1
#James_D Thank you for this great link ... i think i finally understand the concept ... but still have a small problem with implementing it ... for example in my authentication method after the user is authenticated i want to check if that user has an open shift ... so following your link i changed the method to this:
private boolean isShiftOpen(int userId, int branchId, int comId) throws SQLException, ClassNotFoundException {
// final boolean success = false;
Task<Shift> task = new Task<Shift>(){
#Override
protected Shift call() throws Exception {
return ShiftDAO.getShift(userId, branchId, comId);
}
};
task.setOnFailed(e -> {
System.out.println("isShiftOpenTask Faild!!");
success = false;
});
task.setOnSucceeded(e -> {
System.out.println("isShiftOpenTask Succeeded!!");
Shift shift1 = task.getValue();
System.out.println("User Open Shift Exists ... returning true");
SessionBean.setShiftId(shift1.getShiftId());
SessionBean.setUserId(shift1.getUserId());
SessionBean.setUserBranch(branchId);
success = true;
});
exec.execute(task);
return success;
}
I have two problems:
1- The exec.execute(task) raise a nullpoint exception.
2- I wanted to use a boolean variable returned by this method ... the only way i could access such a variable is to define it outside the method ... but then if i want to use another method like this one i must declare another boolean variable for it to ... does this sound right to you?
Thank you for your time
Gado
You call
task.run();
which executes the task on the current thread, i.e. that statement will not complete until the task finishes. Then you call
task.setOnSucceeded(...);
which essentially says "when the task succeeds, set the instance variable rs to the result of the task. However, by the time you call this, the task has already succeeded (or possibly failed), so there is no way for the handler to be invoked.
You could fix the null result by reversing the order of these calls, i.e. do
public ResultSet execQuery(PreparedStatement stmnt) throws SQLException {
Task<ResultSet> task = new Task<ResultSet>() {
#Override protected ResultSet call() throws Exception {
if (isCancelled()) {
}
ResultSet execRs = stmnt.executeQuery();
return execRs;
}
};
task.setOnSucceeded(new EventHandler<WorkerStateEvent>(){
#Override
public void handle(WorkerStateEvent event) {
rs = task.getValue();
}
});
task.run();
return rs;
}
However, since you are executing the task on the current thread, it's not really clear what the point of using a task at all is: you may as well just execute the database query directly in your execQuery method and return the result directly. In other words, the above code is equivalent to
public ResultSet execQuery(PreparedStatement stmnt) throws SQLException {
rs = stmnt.executeQuery();
return rs;
}

JavaFX - Call "updateMessage" for TextArea from background Task - Two problems found

I am having two problems when trying to use "updateMessage" in a JavaFX task.
Issue #1
seems to be a known behavior, but I am not yet sure how exactly I can workaround it.This one is not (yet) critical to me.
The problem is that not all the updates I am performing in a background Task are displayed in the UI (at least the UI does not hang/freezes anymore, which was my initial issue).
My Code of the UI part:
TextArea console = new TextArea();
Button button01 = new Button("Start");
button01.setOnAction(new EventHandler() {
#Override
public void handle(Event event) {
if (ms.getState() == State.READY) {
ms.messageProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
console.appendText(newValue+"\n");
}
});
ms.start();
}
}
});
My Service:
public class MyService extends Service<Object> {
#Override
protected Task createTask() {
//here we use "MyTask" first to show problem #1
MyTask ct = new MyTask();
//here we use "MyTask2" first to show problem #2
// MyTask2 ct = new MyTask2();
try {
ct.call();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("MyService end");
return ct;
}
}
My Task (#1)
public class MyTask extends Task<Object> {
#Override
public EventHandler<WorkerStateEvent> call() {
System.out.println("call() is called");
if (Thread.currentThread().getName().equals("JavaFX Application Thread")){//yes, this might not be right, but if I do not do this, my stuff is executed twice because "call()" is called twice, but the textarea area is just updated in the second run (the non javafx application thread).
return null;
} else{
//actually here I want to do some 'heavy' stuff in the background
//and many things of this heavy stuff should be displayed / logged within the UI
//but very likely (hopefully) new messages (updateMessage) will not be send as fast as in the following loop
for (int i=0;i<10000000;i++){
updateMessage("This is update number'"+i+"' from the background thread");
}
Platform.runLater(new Runnable() {
#Override
public void run() {
try{
//here is the chance to get back to the view
}finally{
}
}
});
return null;
}
}
This basically works, but not every single loop is displayed in the UI.
How do I (correctly) make sure every loop is displayed?
Screenshot: Messages are displayed but not for every loop
Issue #2
Currently blocks my attempt to bring my little text-based game into a JavaFX application.
The main problem is that I am able to call "updateMessage" from the Task directly (see above), but not from a another (sub-)class which I would need to bring all message updates from my game (each message describes the progress of the game) to the UI.
The Task I use (Task #2):
public class MyTask2 extends Task<Object> {
#Override
public EventHandler<WorkerStateEvent> call() {
// ...
UITools myTools = new UITools();
myTools.logToUITest("Just one simple message");
// ...
Platform.runLater(new Runnable() {
#Override
public void run() {
try{
//here is the chance to get back to the view
}finally{
}
}
});
return null;
}
and the (sub-)class that I want to use to do the updateMessage (actually in my little game there would be even more classes that are called during the game and almost all of them trigger an update/message).
public class UITools {
public void logToUITest(String message){
updateMessage(message);
//how to allow 'updateMessage' from the Task to be executed from here?
}
This already results in "The method updateMessage(String) is undefined...".
How could I make it possible to call the updateMessage outside of the Task itself?
updateMessage() can only be called from within the call() method of a Task. It's a constraint imposed by the design of the Task class.
The missed message updates are due to the fact that there are too many updates and not all of them are forwarded to the event queue. Try to reduce the number of updates or sleep for a little while to separate them out in time

Synchronous responses to `Gdx.net.sendHttpRequest` in LibGDX

I'm making a small game in LibGDX. I'm saving the player's username locally as well as on a server. The problem is that the application is not waiting for the result of the call so the online database's ID is not saved locally. Here's the overall flow of the code:
//Create a new user object
User user = new User(name);
//Store the user in the online database
NetworkService networkService = new NetworkService();
String id = networkService.saveUser(user);
//Set the newly generated dbase ID on the local object
user.setId(id);
//Store the user locally
game.getUserService().persist(user);
in this code, the id variable is not getting set because the saveUser function is returning immediately. How can I make the application wait for the result of the network request so I can work with results from the server communication?
This is the code for saveUser:
public String saveUser(User user) {
Map<String, String> parameters = new HashMap<String, String>();
parameters.put("action", "save_user");
parameters.put("json", user.toJSON());
HttpRequest httpGet = new HttpRequest(HttpMethods.POST);
httpGet.setUrl("http://localhost:8080/provisioner");
httpGet.setContent(HttpParametersUtils.convertHttpParameters(parameters));
WerewolfsResponseListener responseListener = new WerewolfsResponseListener();
Gdx.net.sendHttpRequest (httpGet, responseListener);
return responseListener.getLastResponse();
}
This is the WerewolfsResponseListener class:
class WerewolfsResponseListener implements HttpResponseListener {
private String lastResponse = "";
public void handleHttpResponse(HttpResponse httpResponse) {
System.out.println(httpResponse.getResultAsString());
this.lastResponse = httpResponse.getResultAsString();
}
public void failed(Throwable t) {
System.out.println("Saving user failed: "+t.getMessage());
this.lastResponse = null;
}
public String getLastResponse() {
return lastResponse;
}
}
The asynchrony you are seeing is from Gdx.net.sendHttpRequest. The methods on the second parameter (your WerewolfsResponseListener) will be invoked whenever the request comes back. The success/failure methods will not be invoked "inline".
There are two basic approaches for dealing with callbacks structured like this: "polling" or "events".
With polling, your main game loop could "check" the responseListener to see if its succeeded or failed. (You would need to modify your current listener a bit to disambiguate the success case and the empty string.) Once you see a valid response, you can then do the user.setId() and such.
With "events" then you can just put the user.setId() call inside the responseListener callback, so it will be executed whenever the network responds. This is a bit more of a natural fit to the Libgdx net API. (It does mean your response listener will need a reference to the user object.)
It is not possible to "wait" inline for the network call to return. The Libgdx network API (correctly) assumes you do not want to block indefinitely in your render thread, so its not structured for that (the listener will be queued up as a Runnable, so the earliest it can run is on the next render call).
I would not recommend this to any human being, but if you need to test something in a quick and dirty fashion and absolutely must block, this will work. There's no timeout, so again, be prepared for absolute filth:
long wait = 10;
while(!listener.isDone())
{
Gdx.app.log("Net", "Waiting for response");
try
{
Thread.sleep(wait *= 2);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
public static class BlockingResponseListener implements HttpResponseListener
{
private String data;
private boolean done = false;
private boolean succeeded = false;
#Override
public void handleHttpResponse(HttpResponse httpResponse)
{
Gdx.app.log("Net", "response code was "+httpResponse.getStatus().getStatusCode());
data = httpResponse.getResultAsString();
succeeded = true;
done = true;
}
#Override
public void failed(Throwable t)
{
done = true;
succeeded = false;
Gdx.app.log("Net", "Failed due to exception ["+t.getMessage()+"]");
}
public boolean succeeded()
{
return succeeded;
}
public boolean isDone()
{
return done;
}
public String getData()
{
return data;
}
}

Resources