forked from j62/ctbrec
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:
parent
17f1c3aec6
commit
7e2924d780
|
@ -5,6 +5,7 @@
|
||||||
* media player isn't working because of their authetication mechanism
|
* media player isn't working because of their authetication mechanism
|
||||||
* Fixed bug in recorder servlet. Actions for unpin and notes were mixed up
|
* Fixed bug in recorder servlet. Actions for unpin and notes were mixed up
|
||||||
* Recordings now start immediately for newly added models
|
* Recordings now start immediately for newly added models
|
||||||
|
* Added confirmation dialog for "Pause All" and "Resume All"
|
||||||
|
|
||||||
3.8.6
|
3.8.6
|
||||||
========================
|
========================
|
||||||
|
|
|
@ -8,6 +8,8 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -70,6 +72,7 @@ import javafx.beans.value.ChangeListener;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
import javafx.scene.control.TabPane;
|
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());
|
Alert shutdownInfo = new AutosizeAlert(Alert.AlertType.INFORMATION, primaryStage.getScene());
|
||||||
shutdownInfo.setTitle("Shutdown");
|
shutdownInfo.setTitle("Shutdown");
|
||||||
shutdownInfo.setContentText("Shutting down. Please wait while recordings are finished...");
|
shutdownInfo.setContentText("Shutting down. Please wait while recordings are finished...");
|
||||||
shutdownInfo.show();
|
shutdownInfo.show();
|
||||||
|
|
||||||
|
final boolean immediately = shutdownNow;
|
||||||
new Thread() {
|
new Thread() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
modelsTab.saveState();
|
modelsTab.saveState();
|
||||||
recordingsTab.saveState();
|
recordingsTab.saveState();
|
||||||
onlineMonitor.shutdown();
|
onlineMonitor.shutdown();
|
||||||
recorder.shutdown();
|
recorder.shutdown(immediately);
|
||||||
for (Site site : sites) {
|
for (Site site : sites) {
|
||||||
if(site.isEnabled()) {
|
if(site.isEnabled()) {
|
||||||
site.shutdown();
|
site.shutdown();
|
||||||
|
|
|
@ -11,6 +11,7 @@ import javafx.geometry.Insets;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.Alert.AlertType;
|
import javafx.scene.control.Alert.AlertType;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.ButtonType;
|
import javafx.scene.control.ButtonType;
|
||||||
import javafx.scene.control.Dialog;
|
import javafx.scene.control.Dialog;
|
||||||
import javafx.scene.control.TextArea;
|
import javafx.scene.control.TextArea;
|
||||||
|
@ -112,4 +113,19 @@ public class Dialogs {
|
||||||
confirm.showAndWait();
|
confirm.showAndWait();
|
||||||
return confirm.getResult() == ButtonType.YES;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -410,63 +410,65 @@ public class NextGenLocalRecorder implements Recorder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown(boolean immediately) {
|
||||||
// TODO add a config flag for waitign or stopping immediately
|
// TODO add a config flag for waitign or stopping immediately
|
||||||
LOG.info("Shutting down");
|
LOG.info("Shutting down");
|
||||||
recording = false;
|
recording = false;
|
||||||
|
|
||||||
LOG.debug("Stopping all recording processes");
|
if (!immediately) {
|
||||||
recorderLock.lock();
|
LOG.debug("Stopping all recording processes");
|
||||||
try {
|
recorderLock.lock();
|
||||||
// make a copy to avoid ConcurrentModificationException
|
try {
|
||||||
List<Recording> toStop = new ArrayList<>(recordingProcesses.values());
|
// make a copy to avoid ConcurrentModificationException
|
||||||
if (!toStop.isEmpty()) {
|
List<Recording> toStop = new ArrayList<>(recordingProcesses.values());
|
||||||
ExecutorService shutdownPool = Executors.newFixedThreadPool(toStop.size());
|
if (!toStop.isEmpty()) {
|
||||||
List<Future<?>> shutdownFutures = new ArrayList<>(toStop.size());
|
ExecutorService shutdownPool = Executors.newFixedThreadPool(toStop.size());
|
||||||
for (Recording rec : toStop) {
|
List<Future<?>> shutdownFutures = new ArrayList<>(toStop.size());
|
||||||
Optional.ofNullable(rec.getDownload()).ifPresent(d -> {
|
for (Recording rec : toStop) {
|
||||||
shutdownFutures.add(shutdownPool.submit(() -> d.stop()));
|
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();
|
} finally {
|
||||||
try {
|
recorderLock.unlock();
|
||||||
shutdownPool.awaitTermination(10, TimeUnit.MINUTES);
|
}
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
// 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
|
// shutdown threadpools
|
||||||
LOG.info("Waiting for downloads to finish");
|
try {
|
||||||
for (int i = 0; i < 60; i++) {
|
LOG.info("Shutting down download pool");
|
||||||
if (!recordingProcesses.isEmpty()) {
|
downloadPool.shutdown();
|
||||||
try {
|
client.shutdown();
|
||||||
Thread.sleep(1000);
|
downloadPool.awaitTermination(1, TimeUnit.MINUTES);
|
||||||
} catch (InterruptedException e) {
|
LOG.info("Shutting down post-processing pool");
|
||||||
Thread.currentThread().interrupt();
|
ppPool.shutdown();
|
||||||
LOG.error("Error while waiting for downloads to finish", e);
|
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
|
@Override
|
||||||
|
|
|
@ -52,7 +52,7 @@ public interface Recorder {
|
||||||
*/
|
*/
|
||||||
public void unpin(Recording recording) throws IOException, InvalidKeyException, NoSuchAlgorithmException;
|
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 suspendRecording(Model model) throws IOException, InvalidKeyException, NoSuchAlgorithmException;
|
||||||
public void resumeRecording(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();
|
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
|
* @return
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* @throws IllegalStateException
|
* @throws IllegalStateException
|
||||||
|
|
|
@ -175,7 +175,7 @@ public class RemoteRecorder implements Recorder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown(boolean immediately) {
|
||||||
syncThread.stopThread();
|
syncThread.stopThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,7 +151,7 @@ public class HttpServer {
|
||||||
onlineMonitor.shutdown();
|
onlineMonitor.shutdown();
|
||||||
}
|
}
|
||||||
if (recorder != null) {
|
if (recorder != null) {
|
||||||
recorder.shutdown();
|
recorder.shutdown(false);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
server.stop();
|
server.stop();
|
||||||
|
|
Loading…
Reference in New Issue