forked from j62/ctbrec
1
0
Fork 0

Add confirmation dialog for shutdown

Show confirmation dialog when a shutdown is requested and there are
recordings in progress. The user now can decide to shutdown immediately,
shutdown gracefully or leave the app running
This commit is contained in:
0xb00bface 2020-08-19 12:45:47 +02:00
parent 17f1c3aec6
commit 7e2924d780
7 changed files with 90 additions and 52 deletions

View File

@ -5,6 +5,7 @@
* media player isn't working because of their authetication mechanism
* Fixed bug in recorder servlet. Actions for unpin and notes were mixed up
* Recordings now start immediately for newly added models
* Added confirmation dialog for "Pause All" and "Resume All"
3.8.6
========================

View File

@ -8,6 +8,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@ -70,6 +72,7 @@ import javafx.beans.value.ChangeListener;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
@ -249,18 +252,34 @@ public class CamrecApplication extends Application {
}
}
// check for active recordings
boolean shutdownNow = false;
try {
if (!recorder.getCurrentlyRecording().isEmpty()) {
ButtonType result = Dialogs.showShutdownDialog(primaryStage.getScene());
if(result == ButtonType.NO) {
return;
} else if(result == ButtonType.YES) {
shutdownNow = true;
}
}
} catch (InvalidKeyException | NoSuchAlgorithmException | IOException ex) {
LOG.warn("Couldn't check, if recordings are running");
}
Alert shutdownInfo = new AutosizeAlert(Alert.AlertType.INFORMATION, primaryStage.getScene());
shutdownInfo.setTitle("Shutdown");
shutdownInfo.setContentText("Shutting down. Please wait while recordings are finished...");
shutdownInfo.show();
final boolean immediately = shutdownNow;
new Thread() {
@Override
public void run() {
modelsTab.saveState();
recordingsTab.saveState();
onlineMonitor.shutdown();
recorder.shutdown();
recorder.shutdown(immediately);
for (Site site : sites) {
if(site.isEnabled()) {
site.shutdown();

View File

@ -11,6 +11,7 @@ import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.TextArea;
@ -112,4 +113,19 @@ public class Dialogs {
confirm.showAndWait();
return confirm.getResult() == ButtonType.YES;
}
public static ButtonType showShutdownDialog(Scene parent) {
String message = "There are recordings in progress";
AutosizeAlert confirm = new AutosizeAlert(AlertType.CONFIRMATION, "", parent, YES, FINISH, NO);
confirm.setTitle("Shutdown");
confirm.setHeaderText(message);
((Button) confirm.getDialogPane().lookupButton(ButtonType.YES)).setText("Shutdown Now");
((Button) confirm.getDialogPane().lookupButton(ButtonType.YES)).setDefaultButton(false);
((Button) confirm.getDialogPane().lookupButton(ButtonType.FINISH)).setText("Shutdown Gracefully");
((Button) confirm.getDialogPane().lookupButton(ButtonType.FINISH)).setDefaultButton(true);
((Button) confirm.getDialogPane().lookupButton(ButtonType.NO)).setText("Keep Running");
((Button) confirm.getDialogPane().lookupButton(ButtonType.NO)).setDefaultButton(false);
confirm.showAndWait();
return confirm.getResult();
}
}

View File

@ -410,63 +410,65 @@ public class NextGenLocalRecorder implements Recorder {
}
@Override
public void shutdown() {
public void shutdown(boolean immediately) {
// TODO add a config flag for waitign or stopping immediately
LOG.info("Shutting down");
recording = false;
LOG.debug("Stopping all recording processes");
recorderLock.lock();
try {
// make a copy to avoid ConcurrentModificationException
List<Recording> toStop = new ArrayList<>(recordingProcesses.values());
if (!toStop.isEmpty()) {
ExecutorService shutdownPool = Executors.newFixedThreadPool(toStop.size());
List<Future<?>> shutdownFutures = new ArrayList<>(toStop.size());
for (Recording rec : toStop) {
Optional.ofNullable(rec.getDownload()).ifPresent(d -> {
shutdownFutures.add(shutdownPool.submit(() -> d.stop()));
});
if (!immediately) {
LOG.debug("Stopping all recording processes");
recorderLock.lock();
try {
// make a copy to avoid ConcurrentModificationException
List<Recording> toStop = new ArrayList<>(recordingProcesses.values());
if (!toStop.isEmpty()) {
ExecutorService shutdownPool = Executors.newFixedThreadPool(toStop.size());
List<Future<?>> shutdownFutures = new ArrayList<>(toStop.size());
for (Recording rec : toStop) {
Optional.ofNullable(rec.getDownload()).ifPresent(d -> {
shutdownFutures.add(shutdownPool.submit(() -> d.stop()));
});
}
shutdownPool.shutdown();
try {
shutdownPool.awaitTermination(10, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
shutdownPool.shutdown();
try {
shutdownPool.awaitTermination(10, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
recorderLock.unlock();
}
// wait for downloads to finish
LOG.info("Waiting for downloads to finish");
for (int i = 0; i < 60; i++) {
if (!recordingProcesses.isEmpty()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.error("Error while waiting for downloads to finish", e);
}
}
}
} finally {
recorderLock.unlock();
}
// wait for downloads to finish
LOG.info("Waiting for downloads to finish");
for (int i = 0; i < 60; i++) {
if (!recordingProcesses.isEmpty()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.error("Error while waiting for downloads to finish", e);
}
// shutdown threadpools
try {
LOG.info("Shutting down download pool");
downloadPool.shutdown();
client.shutdown();
downloadPool.awaitTermination(1, TimeUnit.MINUTES);
LOG.info("Shutting down post-processing pool");
ppPool.shutdown();
int minutesToWait = 10;
LOG.info("Waiting {} minutes (max) for post-processing to finish", minutesToWait);
ppPool.awaitTermination(minutesToWait, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.error("Error while waiting for pools to finish", e);
}
}
// shutdown threadpools
try {
LOG.info("Shutting down download pool");
downloadPool.shutdown();
client.shutdown();
downloadPool.awaitTermination(1, TimeUnit.MINUTES);
LOG.info("Shutting down post-processing pool");
ppPool.shutdown();
int minutesToWait = 10;
LOG.info("Waiting {} minutes (max) for post-processing to finish", minutesToWait);
ppPool.awaitTermination(minutesToWait, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.error("Error while waiting for pools to finish", e);
}
}
@Override

View File

@ -52,7 +52,7 @@ public interface Recorder {
*/
public void unpin(Recording recording) throws IOException, InvalidKeyException, NoSuchAlgorithmException;
public void shutdown();
public void shutdown(boolean immediately);
public void suspendRecording(Model model) throws IOException, InvalidKeyException, NoSuchAlgorithmException;
public void resumeRecording(Model model) throws IOException, InvalidKeyException, NoSuchAlgorithmException;
@ -66,7 +66,7 @@ public interface Recorder {
public List<Model> getOnlineModels();
/**
* Returns only the models from getModels(), which are actually recorded right now
* Returns only the models from getModels(), which are actually recorded right now (a recording process is currently running).
* @return
* @throws IOException
* @throws IllegalStateException

View File

@ -175,7 +175,7 @@ public class RemoteRecorder implements Recorder {
}
@Override
public void shutdown() {
public void shutdown(boolean immediately) {
syncThread.stopThread();
}

View File

@ -151,7 +151,7 @@ public class HttpServer {
onlineMonitor.shutdown();
}
if (recorder != null) {
recorder.shutdown();
recorder.shutdown(false);
}
try {
server.stop();