Throttle calls to Settings.save()

This commit is contained in:
Sebastian Stenzel
2016-05-03 16:44:22 +02:00
parent b6d1d1dc22
commit 08f664e3df
5 changed files with 46 additions and 33 deletions

View File

@@ -136,6 +136,7 @@ class ExitUtil {
return;
} else {
settings.setNumTrayNotifications(settings.getNumTrayNotifications() - 1);
settings.save();
}
final Runnable notificationCmd;
if (SystemUtils.IS_OS_MAC_OSX) {

View File

@@ -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<? extends Vault> c) -> {
settings.save();
});
// derived bindings:
this.isShowingSettings = activeController.isEqualTo(settingsController.get());

View File

@@ -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;

View File

@@ -48,26 +48,11 @@ public class Settings implements Serializable {
/**
* Package-private constructor; use {@link SettingsProvider}.
*/
Settings() {
this.saveCmd = s -> {
};
}
private Settings(Consumer<Settings> saveCmd) {
Settings(Consumer<Settings> saveCmd) {
this.saveCmd = saveCmd;
}
Settings withSaveCmd(Consumer<Settings> 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<Vault> 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();
}
}

View File

@@ -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<Settings> {
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<Settings> {
}
private final ObjectMapper objectMapper;
private final ScheduledExecutorService saveScheduler = Executors.newSingleThreadScheduledExecutor();
private final AtomicReference<ScheduledFuture<?>> scheduledSaveCmd = new AtomicReference<>();
@Inject
public SettingsProvider(@Named("VaultJsonMapper") ObjectMapper objectMapper) {
@@ -69,23 +78,33 @@ public class SettingsProvider implements Provider<Settings> {
@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());