diff --git a/main/frontend-api/src/main/java/org/cryptomator/frontend/Frontend.java b/main/frontend-api/src/main/java/org/cryptomator/frontend/Frontend.java index 25d0290ca..78978bf7b 100644 --- a/main/frontend-api/src/main/java/org/cryptomator/frontend/Frontend.java +++ b/main/frontend-api/src/main/java/org/cryptomator/frontend/Frontend.java @@ -24,7 +24,10 @@ public interface Frontend extends AutoCloseable { void mount(Map> map) throws CommandFailedException; - void unmount() throws CommandFailedException; + /** + * Unmounts the file system and stops any file system handler threads. + */ + void close() throws Exception; void reveal() throws CommandFailedException; diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavFrontend.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavFrontend.java index a657cc376..f80e5543b 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavFrontend.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavFrontend.java @@ -55,10 +55,10 @@ class WebDavFrontend implements Frontend { mount = webdavMounterProvider.chooseMounter(mountParams).mount(uri, mountParams); } - @Override - public void unmount() throws CommandFailedException { + private void unmount() throws CommandFailedException { if (mount != null) { mount.unmount(); + mount = null; } } diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsWebDavMounter.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsWebDavMounter.java index 931b33ae4..bea6a5cdd 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsWebDavMounter.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsWebDavMounter.java @@ -160,7 +160,7 @@ final class WindowsWebDavMounter implements WebDavMounterStrategy { private WindowsWebDavMount(String driveLetter) { this.driveLetter = CharUtils.toCharacterObject(driveLetter); this.openExplorerScript = fromLines("start explorer.exe " + driveLetter + ":"); - this.unmountScript = fromLines("net use " + driveLetter + ": /delete"); + this.unmountScript = fromLines("net use " + driveLetter + ": /delete /no"); } @Override diff --git a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java index 457cd6d71..b62f631c4 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java @@ -23,6 +23,8 @@ import org.cryptomator.ui.model.VaultObjectMapperProvider; import org.cryptomator.ui.settings.Settings; import org.cryptomator.ui.settings.SettingsProvider; import org.cryptomator.ui.util.DeferredCloser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; @@ -34,6 +36,7 @@ import javafx.stage.Stage; @Module(includes = {CryptoEngineModule.class, CommonsModule.class, WebDavModule.class}) class CryptomatorModule { + private static final Logger LOG = LoggerFactory.getLogger(CryptomatorModule.class); private final Application application; private final Stage mainWindow; @@ -59,7 +62,13 @@ class CryptomatorModule { @Singleton DeferredCloser provideDeferredCloser() { DeferredCloser closer = new DeferredCloser(); - Cryptomator.addShutdownTask(closer::close); + Cryptomator.addShutdownTask(() -> { + try { + closer.close(); + } catch (Exception e) { + LOG.error("Error during shutdown.", e); + } + }); return closer; } diff --git a/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java b/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java index 86c16261b..d28721ffa 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java +++ b/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.util.concurrent.ExecutionException; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.ui.controllers.MainController; @@ -126,7 +127,11 @@ public class MainApplication extends Application { @Override public void stop() { - closer.close(); + try { + closer.close(); + } catch (ExecutionException e) { + LOG.error("Error closing ressources", e); + } } } 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 c0e671d8e..3d0e58aa4 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 @@ -117,12 +117,11 @@ public class UnlockedController extends LocalizedFXMLViewController { @FXML private void didClickLockVault(ActionEvent event) { asyncTaskService.asyncTaskOf(() -> { - vault.get().unmount(); vault.get().deactivateFrontend(); - }).onError(CommandFailedException.class, () -> { - messageLabel.setText(localization.getString("unlocked.label.unmountFailed")); - }).andFinally(() -> { + }).onSuccess(() -> { listener.ifPresent(listener -> listener.didLock(this)); + }).onError(Exception.class, () -> { + messageLabel.setText(localization.getString("unlocked.label.unmountFailed")); }).run(); } 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 3647e8054..07c83530d 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 @@ -148,7 +148,7 @@ public class Vault implements CryptoFileSystemDelegate { } } - public synchronized void deactivateFrontend() { + public synchronized void deactivateFrontend() throws Exception { filesystemFrontend.close(); statsFileSystem = Optional.empty(); Platform.runLater(() -> unlocked.set(false)); @@ -168,10 +168,6 @@ public class Vault implements CryptoFileSystemDelegate { Optionals.ifPresent(filesystemFrontend.get(), Frontend::reveal); } - public void unmount() throws CommandFailedException { - Optionals.ifPresent(filesystemFrontend.get(), Frontend::unmount); - } - // ****************************************************************************** // Delegate methods // ********************************************************************************/ diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/DeferredClosable.java b/main/ui/src/main/java/org/cryptomator/ui/util/DeferredClosable.java index fadb9f7dc..fa8c415ab 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/DeferredClosable.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/DeferredClosable.java @@ -28,12 +28,6 @@ public interface DeferredClosable extends AutoCloseable { */ public Optional get(); - /** - * Quietly closes the Object. If the object was closed before, nothing - * happens. - */ - public void close(); - /** * @return an empty object. */ diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java b/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java index c738e9f59..f2de9324f 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java @@ -13,12 +13,10 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; import org.cryptomator.common.ConsumerThrowingException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; @@ -31,7 +29,7 @@ import com.google.common.annotations.VisibleForTesting; * *

* If you have a {@link DeferredCloser} instance present, call - * {@link #closeLater(Object, Closer)} immediately after you have opened the + * {@link #closeLater(Object, ConsumerThrowingException)} immediately after you have opened the * resource and return a resource handle. If {@link #close()} is called, the * resource will be closed. Calling {@link DeferredClosable#close()} on the resource * handle will also close the resource and prevent a second closing by @@ -42,8 +40,6 @@ import com.google.common.annotations.VisibleForTesting; */ public class DeferredCloser implements AutoCloseable { - private static final Logger LOG = LoggerFactory.getLogger(DeferredCloser.class); - @VisibleForTesting final Map> cleanups = new ConcurrentSkipListMap<>(); @@ -51,33 +47,32 @@ public class DeferredCloser implements AutoCloseable { final AtomicLong counter = new AtomicLong(); private class ManagedResource implements DeferredClosable { + private final long number = counter.incrementAndGet(); - - private final AtomicReference object = new AtomicReference<>(); + private final T object; private final ConsumerThrowingException closer; + private boolean closed = false; public ManagedResource(T object, ConsumerThrowingException closer) { super(); - this.object.set(object); - this.closer = closer; + this.object = Objects.requireNonNull(object); + this.closer = Objects.requireNonNull(closer); } @Override - public void close() { - final T oldObject = object.getAndSet(null); - if (oldObject != null) { - cleanups.remove(number); - try { - closer.accept(oldObject); - } catch (Exception e) { - LOG.error("Closing resource failed.", e); - } - } + public synchronized void close() throws Exception { + closer.accept(object); + cleanups.remove(number); + closed = true; } @Override public Optional get() throws IllegalStateException { - return Optional.ofNullable(object.get()); + if (closed) { + return Optional.empty(); + } else { + return Optional.of(object); + } } } @@ -85,11 +80,23 @@ public class DeferredCloser implements AutoCloseable { * Closes all added objects which have not been closed before and releases references. */ @Override - public void close() { + public void close() throws ExecutionException { + ExecutionException exception = null; for (Iterator> iterator = cleanups.values().iterator(); iterator.hasNext();) { final ManagedResource closableProvider = iterator.next(); - closableProvider.close(); - iterator.remove(); + try { + closableProvider.close(); + iterator.remove(); + } catch (Exception e) { + if (exception == null) { + exception = new ExecutionException(e); + } else { + exception.addSuppressed(e); + } + } + } + if (exception != null) { + throw exception; } }