As the summary says: I have a Service, which in some particular case does not succeed, even though the Task it's running does.
Since I can't seem to attach any files, I'm adding the source code to reproduce below (just copy everything into a file Testcase.java & run as a JavaFX application). This program shows 4 buttons: each button starts a service: a new Stage will show up (which loads http://www.oracle.com in a WebView) & after 2 seconds it will close again & the Task will succeed, printing 2 lines of output: 1 from "succeeded" in Task, 1 from a ChangeListener on the state property of Service.
Now the first button is problematic: sometimes the ChangeListener does not receive the state transition to State.SUCCEEDED. All other buttons work correctly.
import java.util.concurrent.CountDownLatch;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.Worker.State;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class Testcase extends Application {
@Override
public void start(Stage stage) throws Exception {
Button btn1 = new Button("1 load web page & create service in handler");
btn1.setOnAction(e -> createService(true).restart());
Service<Integer> svc2 = createService(true);
Button btn2 = new Button("2 load web page & create service outside handler");
btn2.setOnAction(e -> svc2.restart());
Button btn3 = new Button("3 don't load web page & create service in handler");
btn3.setOnAction(e -> createService(false).restart());
Service<Integer> svc4 = createService(false);
Button btn4 = new Button("4 don't load web page & create service outside handler");
btn4.setOnAction(e -> svc4.restart());
stage.setTitle("BugDemo");
stage.setScene(new Scene(new VBox(btn1, btn2, btn3, btn4)));
stage.show();
}
private Service<Integer> createService(boolean loadWebPage) {
Service<Integer> result = new Service<Integer>() {
@Override
protected Task<Integer> createTask() {
return new BugTask(loadWebPage);
}
};
result.stateProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == State.SUCCEEDED) {
System.out.format("%s -> %s: %s%n", oldValue, newValue, result.getValue());
}
});
return result;
}
}
class BugTask extends Task<Integer> {
private final boolean loadWebPage;
private CountDownLatch latch;
private Stage stage;
private WebView webView;
BugTask(boolean loadWebPage) {
this.loadWebPage = loadWebPage;
}
@Override
protected synchronized Integer call() {
try {
this.latch = new CountDownLatch(1);
Platform.runLater(() -> {
this.webView = new WebView();
this.stage = new Stage();
stage.setScene(new Scene(webView));
stage.show();
if (loadWebPage) {
webView.getEngine().load("http://www.oracle.com");
}
});
new Thread(() -> {
try {
Thread.sleep(2000);
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
Platform.runLater(() -> {
stage.close();
});
}
return 0;
}
@Override
protected void succeeded() {
System.out.println("BugTask succeeded!");
}
}
Since I can't seem to attach any files, I'm adding the source code to reproduce below (just copy everything into a file Testcase.java & run as a JavaFX application). This program shows 4 buttons: each button starts a service: a new Stage will show up (which loads http://www.oracle.com in a WebView) & after 2 seconds it will close again & the Task will succeed, printing 2 lines of output: 1 from "succeeded" in Task, 1 from a ChangeListener on the state property of Service.
Now the first button is problematic: sometimes the ChangeListener does not receive the state transition to State.SUCCEEDED. All other buttons work correctly.
import java.util.concurrent.CountDownLatch;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.Worker.State;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class Testcase extends Application {
@Override
public void start(Stage stage) throws Exception {
Button btn1 = new Button("1 load web page & create service in handler");
btn1.setOnAction(e -> createService(true).restart());
Service<Integer> svc2 = createService(true);
Button btn2 = new Button("2 load web page & create service outside handler");
btn2.setOnAction(e -> svc2.restart());
Button btn3 = new Button("3 don't load web page & create service in handler");
btn3.setOnAction(e -> createService(false).restart());
Service<Integer> svc4 = createService(false);
Button btn4 = new Button("4 don't load web page & create service outside handler");
btn4.setOnAction(e -> svc4.restart());
stage.setTitle("BugDemo");
stage.setScene(new Scene(new VBox(btn1, btn2, btn3, btn4)));
stage.show();
}
private Service<Integer> createService(boolean loadWebPage) {
Service<Integer> result = new Service<Integer>() {
@Override
protected Task<Integer> createTask() {
return new BugTask(loadWebPage);
}
};
result.stateProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == State.SUCCEEDED) {
System.out.format("%s -> %s: %s%n", oldValue, newValue, result.getValue());
}
});
return result;
}
}
class BugTask extends Task<Integer> {
private final boolean loadWebPage;
private CountDownLatch latch;
private Stage stage;
private WebView webView;
BugTask(boolean loadWebPage) {
this.loadWebPage = loadWebPage;
}
@Override
protected synchronized Integer call() {
try {
this.latch = new CountDownLatch(1);
Platform.runLater(() -> {
this.webView = new WebView();
this.stage = new Stage();
stage.setScene(new Scene(webView));
stage.show();
if (loadWebPage) {
webView.getEngine().load("http://www.oracle.com");
}
});
new Thread(() -> {
try {
Thread.sleep(2000);
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
Platform.runLater(() -> {
stage.close();
});
}
return 0;
}
@Override
protected void succeeded() {
System.out.println("BugTask succeeded!");
}
}
- duplicates
-
JDK-8092492 Service can be GC'ed before it fulfills its contract
-
- Closed
-