|
@@ -27,6 +27,7 @@ import java.util.concurrent.ScheduledExecutorService;
|
|
|
import java.util.concurrent.ScheduledFuture;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
+import java.util.stream.Stream;
|
|
|
|
|
|
import javax.inject.Inject;
|
|
|
import javax.inject.Provider;
|
|
@@ -34,6 +35,7 @@ import javax.inject.Singleton;
|
|
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
import org.apache.commons.lang3.SystemUtils;
|
|
|
+import org.cryptomator.common.Environment;
|
|
|
import org.cryptomator.common.LazyInitializer;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
@@ -45,92 +47,75 @@ import com.google.gson.GsonBuilder;
|
|
|
public class SettingsProvider implements Provider<Settings> {
|
|
|
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
|
|
|
- private static final Path DEFAULT_SETTINGS_PATH;
|
|
|
private static final long SAVE_DELAY_MS = 1000;
|
|
|
|
|
|
- static {
|
|
|
- final FileSystem fs = FileSystems.getDefault();
|
|
|
- if (SystemUtils.IS_OS_WINDOWS) {
|
|
|
- DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, "AppData/Roaming/Cryptomator/settings.json");
|
|
|
- } else if (SystemUtils.IS_OS_MAC_OSX) {
|
|
|
- DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, "Library/Application Support/Cryptomator/settings.json");
|
|
|
- } else {
|
|
|
- DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, ".Cryptomator/settings.json");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
private final ScheduledExecutorService saveScheduler = Executors.newSingleThreadScheduledExecutor();
|
|
|
private final AtomicReference<ScheduledFuture<?>> scheduledSaveCmd = new AtomicReference<>();
|
|
|
private final AtomicReference<Settings> settings = new AtomicReference<>();
|
|
|
private final SettingsJsonAdapter settingsJsonAdapter = new SettingsJsonAdapter();
|
|
|
+ private final Environment env;
|
|
|
private final Gson gson;
|
|
|
|
|
|
@Inject
|
|
|
- public SettingsProvider() {
|
|
|
+ public SettingsProvider(Environment env) {
|
|
|
+ this.env = env;
|
|
|
this.gson = new GsonBuilder() //
|
|
|
.setPrettyPrinting().setLenient().disableHtmlEscaping() //
|
|
|
.registerTypeAdapter(Settings.class, settingsJsonAdapter) //
|
|
|
.create();
|
|
|
}
|
|
|
|
|
|
- private Path getSettingsPath() {
|
|
|
- final String settingsPathProperty = System.getProperty("cryptomator.settingsPath");
|
|
|
- return Optional.ofNullable(settingsPathProperty).filter(StringUtils::isNotBlank).map(this::replaceHomeDir).map(FileSystems.getDefault()::getPath).orElse(DEFAULT_SETTINGS_PATH);
|
|
|
- }
|
|
|
-
|
|
|
- private String replaceHomeDir(String path) {
|
|
|
- if (path.startsWith("~/")) {
|
|
|
- return SystemUtils.USER_HOME + path.substring(1);
|
|
|
- } else {
|
|
|
- return path;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
@Override
|
|
|
public Settings get() {
|
|
|
return LazyInitializer.initializeLazily(settings, this::load);
|
|
|
}
|
|
|
|
|
|
private Settings load() {
|
|
|
- Settings settings;
|
|
|
- final Path settingsPath = getSettingsPath();
|
|
|
- try (InputStream in = Files.newInputStream(settingsPath, StandardOpenOption.READ); //
|
|
|
- Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
|
|
|
- settings = gson.fromJson(reader, Settings.class);
|
|
|
+ Settings settings = env.getSettingsPath().flatMap(this::tryLoad).findFirst().orElse(new Settings());
|
|
|
+ settings.setSaveCmd(this::scheduleSave);
|
|
|
+ return settings;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Stream<Settings> tryLoad(Path path) {
|
|
|
+ LOG.debug("Attempting to load settings from {}", path);
|
|
|
+ try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ); //
|
|
|
+ Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
|
|
|
+ Settings settings = gson.fromJson(reader, Settings.class);
|
|
|
if (settings == null) {
|
|
|
throw new IOException("Unexpected EOF");
|
|
|
}
|
|
|
- LOG.info("Settings loaded from " + settingsPath);
|
|
|
+ LOG.info("Settings loaded from {}", path);
|
|
|
+ return Stream.of(settings);
|
|
|
} catch (IOException e) {
|
|
|
LOG.info("Failed to load settings, creating new one.");
|
|
|
- settings = new Settings();
|
|
|
+ return Stream.empty();
|
|
|
}
|
|
|
- settings.setSaveCmd(this::scheduleSave);
|
|
|
- return 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);
|
|
|
- }
|
|
|
+ final Optional<Path> settingsPath = env.getSettingsPath().findFirst(); // alway save to preferred (first) path
|
|
|
+ settingsPath.ifPresent(path -> {
|
|
|
+ Runnable saveCommand = () -> this.save(settings, path);
|
|
|
+ ScheduledFuture<?> scheduledTask = saveScheduler.schedule(saveCommand, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
|
|
|
+ ScheduledFuture<?> previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask);
|
|
|
+ if (previouslyScheduledTask != null) {
|
|
|
+ previouslyScheduledTask.cancel(false);
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
- private void save(Settings settings) {
|
|
|
+ private void save(Settings settings, Path settingsPath) {
|
|
|
assert settings != null : "method should only be invoked by #scheduleSave, which checks for null";
|
|
|
- final Path settingsPath = getSettingsPath();
|
|
|
+ LOG.debug("Attempting to save settings to {}", settingsPath);
|
|
|
try {
|
|
|
Files.createDirectories(settingsPath.getParent());
|
|
|
try (OutputStream out = Files.newOutputStream(settingsPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); //
|
|
|
Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
|
|
|
gson.toJson(settings, writer);
|
|
|
- LOG.info("Settings saved to " + settingsPath);
|
|
|
+ LOG.info("Settings saved to {}", settingsPath);
|
|
|
}
|
|
|
} catch (IOException e) {
|
|
|
LOG.error("Failed to save settings.", e);
|