From 08f664e3df6ae46641ed602cb7f0cb2efa005d85 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 3 May 2016 16:44:22 +0200 Subject: [PATCH] Throttle calls to Settings.save() --- .../java/org/cryptomator/ui/ExitUtil.java | 1 + .../ui/controllers/MainController.java | 4 +++ .../ui/controllers/SettingsController.java | 21 ++++++++++---- .../org/cryptomator/ui/settings/Settings.java | 24 ++------------- .../ui/settings/SettingsProvider.java | 29 +++++++++++++++---- 5 files changed, 46 insertions(+), 33 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java b/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java index 39cb090e3..3c4976cf4 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java +++ b/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java @@ -136,6 +136,7 @@ class ExitUtil { return; } else { settings.setNumTrayNotifications(settings.getNumTrayNotifications() - 1); + settings.save(); } final Runnable notificationCmd; if (SystemUtils.IS_OS_MAC_OSX) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java index 5c262f6de..f316fe2c2 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java @@ -40,6 +40,7 @@ import javafx.beans.binding.BooleanBinding; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener.Change; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; @@ -94,6 +95,9 @@ public class MainController extends LocalizedFXMLViewController { this.changePasswordController = changePasswordController; this.settingsController = settingsController; this.vaults = FXCollections.observableList(settings.getDirectories()); + this.vaults.addListener((Change c) -> { + settings.save(); + }); // derived bindings: this.isShowingSettings = activeController.isEqualTo(settingsController.get()); diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java index 7af0e8269..80cd60cab 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java @@ -59,9 +59,9 @@ public class SettingsController extends LocalizedFXMLViewController { useIpv6Checkbox.setSelected(SystemUtils.IS_OS_WINDOWS && settings.shouldUseIpv6()); versionLabel.setText(String.format(localization.getString("settings.version.label"), applicationVersion().orElse("SNAPSHOT"))); - EasyBind.subscribe(checkForUpdatesCheckbox.selectedProperty(), settings::setCheckForUpdatesEnabled); + EasyBind.subscribe(checkForUpdatesCheckbox.selectedProperty(), this::checkForUpdateDidChange); EasyBind.subscribe(portField.textProperty(), this::portDidChange); - EasyBind.subscribe(useIpv6Checkbox.selectedProperty(), settings::setUseIpv6); + EasyBind.subscribe(useIpv6Checkbox.selectedProperty(), this::useIpv6DidChange); } @Override @@ -73,21 +73,30 @@ public class SettingsController extends LocalizedFXMLViewController { return Optional.ofNullable(getClass().getPackage().getImplementationVersion()); } + private void checkForUpdateDidChange(Boolean newValue) { + settings.setCheckForUpdatesEnabled(newValue); + settings.save(); + } + private void portDidChange(String newValue) { try { int port = Integer.parseInt(newValue); - if (port < Settings.MIN_PORT) { + if (port < Settings.MIN_PORT || port > Settings.MAX_PORT) { settings.setPort(Settings.DEFAULT_PORT); - } else if (port < Settings.MAX_PORT) { - settings.setPort(port); } else { - portField.setText(String.valueOf(Settings.MAX_PORT)); + settings.setPort(port); + settings.save(); } } catch (NumberFormatException e) { portField.setText(String.valueOf(Settings.DEFAULT_PORT)); } } + private void useIpv6DidChange(Boolean newValue) { + settings.setUseIpv6(newValue); + settings.save(); + } + private void filterNumericKeyEvents(KeyEvent t) { if (t.getCharacter() == null || t.getCharacter().length() == 0) { return; diff --git a/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java b/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java index a2540e003..17f7c5b25 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java +++ b/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java @@ -48,26 +48,11 @@ public class Settings implements Serializable { /** * Package-private constructor; use {@link SettingsProvider}. */ - Settings() { - this.saveCmd = s -> { - }; - } - - private Settings(Consumer saveCmd) { + Settings(Consumer saveCmd) { this.saveCmd = saveCmd; } - Settings withSaveCmd(Consumer saveCmd) { - final Settings result = new Settings(saveCmd); - result.directories = this.directories; - result.checkForUpdatesEnabled = this.checkForUpdatesEnabled; - result.port = this.port; - result.useIpv6 = this.useIpv6; - result.numTrayNotifications = this.numTrayNotifications; - return result; - } - - private void save() { + public void save() { saveCmd.accept(this); } @@ -82,7 +67,6 @@ public class Settings implements Serializable { public void setDirectories(List directories) { this.directories = directories; - save(); } public boolean isCheckForUpdatesEnabled() { @@ -92,7 +76,6 @@ public class Settings implements Serializable { public void setCheckForUpdatesEnabled(boolean checkForUpdatesEnabled) { this.checkForUpdatesEnabled = checkForUpdatesEnabled; - save(); } public void setPort(int port) { @@ -100,7 +83,6 @@ public class Settings implements Serializable { throw new IllegalArgumentException("Invalid port"); } this.port = port; - save(); } public int getPort() { @@ -121,7 +103,6 @@ public class Settings implements Serializable { public void setUseIpv6(boolean useIpv6) { this.useIpv6 = useIpv6; - save(); } public Integer getNumTrayNotifications() { @@ -130,7 +111,6 @@ public class Settings implements Serializable { public void setNumTrayNotifications(Integer numTrayNotifications) { this.numTrayNotifications = numTrayNotifications; - save(); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java b/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java index 1bfddbddf..417b6c369 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java +++ b/main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java @@ -16,6 +16,12 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.util.Objects; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import javax.inject.Inject; import javax.inject.Named; @@ -34,6 +40,7 @@ public class SettingsProvider implements Provider { private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class); private static final Path SETTINGS_DIR; private static final String SETTINGS_FILE = "settings.json"; + private static final long SAVE_DELAY_MS = 1000; static { final String appdata = System.getenv("APPDATA"); @@ -52,6 +59,8 @@ public class SettingsProvider implements Provider { } private final ObjectMapper objectMapper; + private final ScheduledExecutorService saveScheduler = Executors.newSingleThreadScheduledExecutor(); + private final AtomicReference> scheduledSaveCmd = new AtomicReference<>(); @Inject public SettingsProvider(@Named("VaultJsonMapper") ObjectMapper objectMapper) { @@ -69,23 +78,33 @@ public class SettingsProvider implements Provider { @Override public Settings get() { - Settings settings = null; + final Settings settings = new Settings(this::scheduleSave); try { final Path settingsPath = getSettingsPath(); final InputStream in = Files.newInputStream(settingsPath, StandardOpenOption.READ); - settings = objectMapper.readValue(in, Settings.class); + objectMapper.readerForUpdating(settings).readValue(in); LOG.info("Settings loaded from " + settingsPath); } catch (IOException e) { LOG.info("Failed to load settings, creating new one."); - settings = new Settings(); } - return settings.withSaveCmd(this::save); + return settings; } - private void save(Settings settings) { + private void scheduleSave(Settings settings) { if (settings == null) { return; } + ScheduledFuture saveCmd = saveScheduler.schedule(() -> { + this.save(settings); + } , SAVE_DELAY_MS, TimeUnit.MILLISECONDS); + ScheduledFuture previousSaveCmd = scheduledSaveCmd.getAndSet(saveCmd); + if (previousSaveCmd != null) { + previousSaveCmd.cancel(false); + } + } + + private void save(Settings settings) { + Objects.requireNonNull(settings); try { final Path settingsPath = getSettingsPath(); Files.createDirectories(settingsPath.getParent());