The program Triathlon executes a long-running task with the possibility to restart it again if the task has been completely executed. I wanted to add the possibility to stop the execution in order to reset the UI. To that aim, I added a new button, stop. Here is the code:
package triathlon2;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.*;
import javafx.beans.property.*;
import javafx.beans.value.*;
import javafx.concurrent.Task;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Triathlon2 extends Application
{
private final Random random = new Random();
private final ExecutorService exec = Executors.newSingleThreadExecutor();
final TaskMonitor taskMonitor = new TaskMonitor();
final ProgressIndicator progressIndicator
= new ProgressIndicator();
@Override public void start(Stage stage) throws Exception
{
progressIndicator.progressProperty()
.bind(taskMonitor.currentTaskProgressProperty());
final Label currentRaceStage = new Label();
currentRaceStage.textProperty()
.bind(taskMonitor.currentTaskNameProperty());
createMainLayout(
stage,
createStartRaceButton(
exec,
taskMonitor
),
createStopButton(
//exec,
taskMonitor
),
createRaceProgressView(
taskMonitor,
progressIndicator,
currentRaceStage
)
);
}
@Override public void stop() throws Exception
{
exec.shutdownNow();
}
private Button createStartRaceButton(
final ExecutorService exec,
final TaskMonitor taskMonitor)
{
final Button startButton = new Button("Start Race");
startButton.disableProperty()
.bind(taskMonitor.idleProperty().not());
startButton.setOnAction((ActionEvent actionEvent) ->
{
runRace(exec, taskMonitor);
});
return startButton;
}
private Button createStopButton(
//final ExecutorService exec,
final TaskMonitor taskMonitor)
{
final Button stopButton = new Button("Stop Race");
stopButton.disableProperty()
.bind(taskMonitor.idleProperty());
stopButton.setOnAction((ActionEvent actionEvent) ->
{
exec.shutdownNow();
Platform.setImplicitExit(true);
});
return stopButton;
}
private HBox createRaceProgressView(
final TaskMonitor taskMonitor,
ProgressIndicator progressIndicator,
Label currentRaceStage)
{
final HBox raceProgress = new HBox(10);
raceProgress.getChildren().setAll(
currentRaceStage,
progressIndicator
);
raceProgress.setOpacity(0);
raceProgress.setAlignment(Pos.CENTER);
final FadeTransition fade
= new FadeTransition(
Duration.seconds(0.75), raceProgress);
fade.setToValue(0);
taskMonitor.idleProperty()
.addListener((Observable observable) ->
{
if (taskMonitor.idleProperty().get())
{
fade.playFromStart();
} else
{
fade.stop();
raceProgress.setOpacity(1);
}
});
return raceProgress;
}
private void createMainLayout(Stage stage, Button startButton, Button stopButton, HBox raceProgress)
{
final VBox layout = new VBox(10);
layout.getChildren().setAll(
raceProgress,
startButton,stopButton
);
layout.setAlignment(Pos.CENTER);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10px;");
stage.setScene(new Scene(layout, 500, 600));
stage.show();
}
private void runRace(ExecutorService exec, TaskMonitor taskMonitor)
{
StageTask swimTask = new StageTask("Swim", 30, 40);
StageTask bikeTask = new StageTask("Bike", 210, 230);
StageTask runTask = new StageTask("Run", 120, 140);
taskMonitor.monitor(swimTask, bikeTask, runTask);
exec.execute(swimTask);
exec.execute(bikeTask);
exec.execute(runTask);
}
class TaskMonitor {
final private ReadOnlyObjectWrapper<StageTask> currentTask = new ReadOnlyObjectWrapper<>();
final private ReadOnlyStringWrapper currentTaskName = new ReadOnlyStringWrapper();
final private ReadOnlyDoubleWrapper currentTaskProgress = new ReadOnlyDoubleWrapper();
final private ReadOnlyBooleanWrapper idle = new ReadOnlyBooleanWrapper(true);
public void monitor(final StageTask task)
{
task.stateProperty().addListener(new ChangeListener<Task.State>()
{
@Override
public void changed(ObservableValue<? extends Task.State> observableValue, Task.State oldState, Task.State state)
{
switch (state)
{
case RUNNING:
currentTask.set(task);
currentTaskProgress.unbind();
currentTaskProgress.set(task.progressProperty().get());
currentTaskProgress.bind(task.progressProperty());
currentTaskName.set(task.nameProperty().get());
idle.set(false);
break;
case SUCCEEDED:
case CANCELLED:
case FAILED:
task.stateProperty().removeListener(this);
idle.set(true);
break;
}
}
});
}
public void monitor(final StageTask... tasks)
{
for (StageTask task: tasks) {
monitor(task);
}
}
public ReadOnlyObjectProperty<StageTask> currentTaskProperty()
{
return currentTask.getReadOnlyProperty();
}
public ReadOnlyStringProperty currentTaskNameProperty()
{
return currentTaskName.getReadOnlyProperty();
}
public ReadOnlyDoubleProperty currentTaskProgressProperty()
{
return currentTaskProgress.getReadOnlyProperty();
}
public ReadOnlyBooleanProperty idleProperty()
{
return idle.getReadOnlyProperty();
}
}
class StageTask extends Task<Duration>
{
final private ReadOnlyStringWrapper name;
final private int minMinutesElapsed;
final private int maxMinutesElapsed;
public StageTask(String name, int minMinutesElapsed, int maxMinutesElapsed)
{
this.name = new ReadOnlyStringWrapper(name);
this.minMinutesElapsed = minMinutesElapsed;
this.maxMinutesElapsed = maxMinutesElapsed;
}
@Override protected Duration call() throws Exception
{
Duration duration = timeInRange(
minMinutesElapsed, maxMinutesElapsed
);
for (int i = 0; i < 25; i++)
{
updateProgress(i, 25);
Thread.sleep((int) (duration.toMinutes()));
}
updateProgress(25, 25);
return duration;
}
private Duration timeInRange(int min, int max)
{
return Duration.minutes(
random.nextDouble() * (max - min) + min
);
}
public ReadOnlyStringProperty nameProperty()
{
return name.getReadOnlyProperty();
}
}
public static void main(String[] args)
{
Application.launch(Triathlon2.class);
}
}
The program restarts fine if the task has been completed, but it collapses if I call start after having stopped it. What shall I correct?