diff --git a/main/pom.xml b/main/pom.xml
index d11d564b0..3f8b97d42 100644
--- a/main/pom.xml
+++ b/main/pom.xml
@@ -29,7 +29,7 @@
1.1.1
1.2.2
- 0.5.1
+ 0.6.0-SNAPSHOT
1.0.1
2.8.1
1.7.25
diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java
index c0efa6fdf..4b04fd1ed 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java
@@ -8,6 +8,8 @@
******************************************************************************/
package org.cryptomator.ui.controllers;
+import static java.lang.String.format;
+
import java.util.Optional;
import javax.inject.Inject;
@@ -15,7 +17,10 @@ import javax.inject.Inject;
import org.cryptomator.ui.model.Vault;
import org.cryptomator.ui.settings.Localization;
import org.cryptomator.ui.util.AsyncTaskService;
+import org.cryptomator.ui.util.DialogBuilderUtil;
import org.fxmisc.easybind.EasyBind;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
@@ -31,6 +36,8 @@ import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
+import javafx.scene.control.Alert;
+import javafx.scene.control.ButtonType;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
@@ -43,6 +50,8 @@ import javafx.util.Duration;
public class UnlockedController implements ViewController {
+ private static final Logger LOG = LoggerFactory.getLogger(UnlockedController.class);
+
private static final int IO_SAMPLING_STEPS = 100;
private static final double IO_SAMPLING_INTERVAL = 0.25;
@@ -109,16 +118,63 @@ public class UnlockedController implements ViewController {
@FXML
private void didClickLockVault(ActionEvent event) {
+ regularLockVault();
+ }
+
+ private void regularLockVault() {
asyncTaskService.asyncTaskOf(() -> {
vault.get().unmount();
vault.get().lock();
}).onSuccess(() -> {
listener.ifPresent(listener -> listener.didLock(this));
- }).onError(Exception.class, () -> {
- messageLabel.setText(localization.getString("unlocked.label.unmountFailed"));
+ LOG.trace("Regular lock succeeded");
+ }).onError(Exception.class, e -> {
+ onRegularLockVaultFailed(e);
}).run();
}
+ private void forcedLockVault() {
+ asyncTaskService.asyncTaskOf(() -> {
+ vault.get().unmountForced();
+ vault.get().lock();
+ }).onSuccess(() -> {
+ listener.ifPresent(listener -> listener.didLock(this));
+ LOG.trace("Forced lock succeeded");
+ }).onError(Exception.class, e -> {
+ onForcedLockVaultFailed(e);
+ }).run();
+ }
+
+ private void onRegularLockVaultFailed(Exception e) {
+ if (vault.get().supportsForcedUnmount()) {
+ LOG.trace("Regular unmount failed", e);
+ Alert confirmDialog = DialogBuilderUtil.buildYesNoDialog( //
+ format(localization.getString("unlocked.lock.force.confirmation.title"), vault.get().name().getValue()), //
+ localization.getString("unlocked.lock.force.confirmation.header"), //
+ localization.getString("unlocked.lock.force.confirmation.content"), //
+ ButtonType.NO);
+
+ Optional choice = confirmDialog.showAndWait();
+ if (ButtonType.YES.equals(choice.get())) {
+ forcedLockVault();
+ } else {
+ LOG.trace("Unmount cancelled", e);
+ }
+ } else {
+ LOG.error("Regular unmount failed", e);
+ showUnmountFailedMessage();
+ }
+ }
+
+ private void onForcedLockVaultFailed(Exception e) {
+ LOG.error("Forced unmount failed", e);
+ showUnmountFailedMessage();
+ }
+
+ private void showUnmountFailedMessage() {
+ messageLabel.setText(localization.getString("unlocked.label.unmountFailed"));
+ }
+
@FXML
private void didClickMoreOptions(ActionEvent event) {
if (moreOptionsMenu.isShowing()) {
diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java
index 4e84a5f39..d972c3ab6 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java
@@ -23,6 +23,7 @@ import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
+import org.cryptomator.common.ConsumerThrowingException;
import org.cryptomator.common.LazyInitializer;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings;
@@ -146,14 +147,26 @@ public class Vault {
}
public synchronized void unmount() throws Exception {
+ unmount(mount -> mount.unmount());
+ }
+
+ public synchronized void unmountForced() throws Exception {
+ unmount(mount -> mount.forced().get().unmount());
+ }
+
+ private synchronized void unmount(ConsumerThrowingException command) throws CommandFailedException {
if (mount != null) {
- mount.unmount();
+ command.accept(mount);
}
Platform.runLater(() -> {
mounted.set(false);
});
}
+ public boolean supportsForcedUnmount() {
+ return mount != null && mount.forced().isPresent();
+ }
+
public synchronized void lock() throws Exception {
if (servlet != null) {
servlet.stop();
diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/DialogBuilderUtil.java b/main/ui/src/main/java/org/cryptomator/ui/util/DialogBuilderUtil.java
index e9f1b99fe..55f6539a7 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/util/DialogBuilderUtil.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/util/DialogBuilderUtil.java
@@ -34,11 +34,15 @@ public class DialogBuilderUtil {
return buildDialog(title, header, content, Alert.AlertType.CONFIRMATION, defaultButton);
}
- private static Alert buildDialog(String title, String header, String content, Alert.AlertType type, ButtonType defaultButton) {
+ public static Alert buildYesNoDialog(String title, String header, String content, ButtonType defaultButton) {
+ return buildDialog(title, header, content, Alert.AlertType.CONFIRMATION, defaultButton, ButtonType.YES, ButtonType.NO);
+ }
+
+ private static Alert buildDialog(String title, String header, String content, Alert.AlertType type, ButtonType defaultButton, ButtonType... buttons) {
Text contentText = new Text(content);
contentText.setWrappingWidth(360.0);
- Alert alert = new Alert(type);
+ Alert alert = new Alert(type, content, buttons);
alert.setTitle(title);
alert.setHeaderText(header);
alert.getDialogPane().setContent(contentText);
diff --git a/main/ui/src/main/resources/localization/en.txt b/main/ui/src/main/resources/localization/en.txt
index 16468bb52..393faa982 100644
--- a/main/ui/src/main/resources/localization/en.txt
+++ b/main/ui/src/main/resources/localization/en.txt
@@ -96,6 +96,9 @@ unlocked.label.unmountFailed=Ejecting drive failed
unlocked.label.statsEncrypted=encrypted
unlocked.label.statsDecrypted=decrypted
unlocked.ioGraph.yAxis.label=Throughput (MiB/s)
+unlocked.lock.force.confirmation.title=Locking of %1$s failed
+unlocked.lock.force.confirmation.header=Do you want to force locking?
+unlocked.lock.force.confirmation.content=This may be because other programs are still accessing files in the vault or because some other problem occurred.\n\nPrograms still accessing the files may not work correctly and data not already written by those programs may be lost.
# settings.fxml
settings.version.label=Version %s