0

I am writing a program that can update it's self. I created a separate thread that checks the server for updates (as not not block the UI from updating). Once updates are done checking, and it has determined that a newer version is available, it should ask the user if he would like to get those updates.

Here is my shouldApplyUpdates method:

public boolean shouldApplyUpdate() {
    Alert updateAlert = new Alert(Alert.AlertType.CONFIRMATION);
    updateAlert.setTitle(resourceBundle.getString("ui.gui.update.title"));
    updateAlert.setContentText(resourceBundle.getString("ui.gui.update.message"));
    Optional<ButtonType> ret = updateAlert.showAndWait();
    return ret.get() == ButtonType.OK;
}

What it should do is prompt the user if he would like to apply updates, and if so, return true. The problem is, because this method is being called from another thread, an exception is thrown from not being on the JavaFX Application Thread (in my case, silently, so I had to surround the method with try/catch to see the exception).

Here is my update thread:

new Thread(new Task<Void>() {
    @Override
    protected Void call() throws Exception {
        //Check for updates
        ...
        //If updates are available, call shouldApplyUpdates()
        if(shouldApplyUpdates()){
            //Apply the updates
        }
        return null;
    }
}).start();
//Create GUI and stuffs, all should be happening while updates are being checked

So what I need to so is create and show the dialog on the Application Thread, and then block the method from returning (the method is safe to be blocked because of the separate thread). Then after the user confirms his choice, return the method.

What is the best way to accomplish this?

Chris Smith
  • 2,928
  • 4
  • 27
  • 59
  • 2
    You need to interact with the UI ONLY from FX's event dispatching thread, this is (as you've discovered) an absolute required. JavaFX has a `Platform.runLater` option, which will run the supplied request, some time in the future, on the EDT, but, unlike Swing, doesn't have a `runAndWait`, which would be useful in your case, BUT, you could have a look at [this](http://www.guigarage.com/2013/01/invokeandwait-for-javafx/) implementation to see if it will do what you want... – MadProgrammer Sep 22 '15 at 01:46
  • I knew about the `runLater` option, and knew I needed some sort of locking mechanism, I will take a look at that. – Chris Smith Sep 22 '15 at 01:48
  • 1
    @MadProgrammer That link seems unduly complex. You can just pass a `FutureTask` to `Platform.runLater()` and then call `get()` on it to block until it's done. – James_D Sep 22 '15 at 02:23
  • 1
    @James_D I didn't write, I don't even use FX, I just did the research... – MadProgrammer Sep 22 '15 at 02:55
  • Understood... it's more a `java.util.concurrent` API feature than an FX feature though. You could do the same with `SwingUtilities.invokeLater(...)`: I suspect there would be no `SwingUtilities.invokeAndWait(...)` had `FutureTask` existed when Swing was written. – James_D Sep 22 '15 at 02:59

1 Answers1

1

I'm not sure you need to block the method from returning. You can just do this:

Task<Boolean> checkForUpdateTask = new Task<Boolean>() {
    @Override
    public Boolean call() throws Exception {
        // check for updates...
        if (/* updates available */) {
            return true ;
        } else {
            return false ;
        }
    }
};
checkForUpdateTask.setOnSucceeded(e -> {
    if (checkForUpdateTask.getValue() && shouldApplyUpdates()) {
        // apply updates...
    } else {
        // proceed....
    }
});
checkForUpdateTask.setOnFailed(e -> {
    checkForUpdateTask.getException().printStackTrace();
});
new Thread(checkForUpdateTask).start();

If you really do need to block the method from returning, you can use the following idiom. This is based on the solution to JavaFX2: Can I pause a background Task / Service?

Task<Void> checkForUpdateTask = new Task<Void>() {
    @Override
    public Void call() throws Exception {
        // check for updates...
        if (/* updates available */) {
            FutureTask<Boolean> checkUser = new FutureTask<Boolean>(() -> shouldApplyUpdates());
            Platform.runLater(checkUser);

            // checkUser.get() will block until it returns a value...
            if (checkUser.get()) {
                // apply updates...
            }
        }
        return null ;
    }
};
new Thread(checkForUpdateTask).start();

In this version, the // apply updates... code block is executed on the background thread, which may be what you want.

Community
  • 1
  • 1
James_D
  • 201,275
  • 16
  • 291
  • 322
  • I am abstracting it in a way that requires the method to block. I have a commons library that deals with checking for updates and each program/project has it's own implementation of asking the user to apply updates. I could have a runnable passed to the method, or even just call the `getUpdates()` method from the project, but I would rather it just return a boolean. – Chris Smith Sep 22 '15 at 02:24
  • So the second implementation should work for you. Favor higher-level API such as `FutureTask` over low-level locking APIs. – James_D Sep 22 '15 at 02:25
  • I'm trying to understand the second implementation atm. – Chris Smith Sep 22 '15 at 02:25
  • That is ****** awesome! Didn't know it was that simple! – Chris Smith Sep 22 '15 at 02:32
  • 1
    I had the same reaction the first time I saw that. I can't find the similar question now, I think it was @jewelsea who answered it. – James_D Sep 22 '15 at 02:33