Switch to JSON as config format

Java Properties require ISO 8859-1 character encoding, leading to manual edits of file.
This commit is contained in:
Armin Schrenk
2026-01-14 14:12:28 +01:00
parent b1c21501a6
commit 42b06aa556
3 changed files with 59 additions and 23 deletions

View File

@@ -1,21 +1,24 @@
package org.cryptomator.launcher;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* Class to overwrite system properties with an external properties file
* <p>
* To overwrite system properties, the method {@link #adjustSystemProperties()} loads the properties file {@value PROP_FILENAME} from an OS-dependent location and add all supported properties to the {@link System} properties.
* To overwrite system properties, the method {@link #adjustSystemProperties()} loads the JSON file {@value CONFIG_NAME} from an OS-dependent location and add all supported properties to the {@link System} properties.
* The predefined location are:
* <ul>
* <li>Linux - {@value LINUX_DIR }</li>
@@ -37,12 +40,12 @@ import java.util.Set;
*/
class AdminPropertiesSetter {
private static final Logger LOG = LoggerFactory.getLogger(AdminPropertiesSetter.class);
private static final ObjectMapper JSON = JsonMapper.builder().build();
private static final String LINUX_DIR = "/etc/cryptmator";
private static final String MAC_DIR = "/Library/Application Support/Cryptomator";
private static final String WIN_DIR = "%PROGRAMDATA%\\Cryptomator";
private static final String PROP_FILENAME = "config.properties";
private static final String CONFIG_NAME = "config.json";
private static final Set<String> ALLOWED_OVERRIDES = Set.of( //
"cryptomator.logDir", //
"cryptomator.pluginDir", //
@@ -60,7 +63,7 @@ class AdminPropertiesSetter {
} else { //LINUX
adminDir = Path.of(LINUX_DIR);
}
ADMIN_PROPERTIES_FILE = adminDir.resolve(PROP_FILENAME);
ADMIN_PROPERTIES_FILE = adminDir.resolve(CONFIG_NAME);
}
private static final Path ADMIN_PROPERTIES_FILE;
@@ -77,29 +80,34 @@ class AdminPropertiesSetter {
var systemProps = System.getProperties();
var adminProps = loadAdminProperties(ADMIN_PROPERTIES_FILE);
for (var key : adminProps.stringPropertyNames()) {
if (ALLOWED_OVERRIDES.contains(key)) {
var value = adminProps.getProperty(key);
LOG.info("Overwriting {} with value {} from admin properties.", key, value);
systemProps.setProperty(key, value);
adminProps.forEach((key, value) -> {
if (ALLOWED_OVERRIDES.contains(key) && value instanceof String v) {
log("Overwriting {} with value {} from admin properties.", List.of(key, v));
systemProps.setProperty(key, v);
} else {
LOG.debug("Property {} in admin properties is not supported for override.", key);
var reason = value instanceof String ? "Unsupported" : "Not a string";
log("Property {} in admin config ignored: {}.", List.of(key, reason));
}
}
});
return systemProps;
}
private static Properties loadAdminProperties(Path adminPropertiesPath) {
var adminProps = new Properties();
try {
adminProps.load(Files.newInputStream(adminPropertiesPath, StandardOpenOption.READ));
private static Map<String, Object> loadAdminProperties(Path adminPropertiesPath) {
try (var in = Files.newInputStream(adminPropertiesPath, StandardOpenOption.READ)) {
var map = JSON.readValue(in, new TypeReference<Map<String, Object>>() {});
if (map != null) {
return map;
}
} catch (NoSuchFileException _) {
//NO-OP
LOG.debug("No admin properties found at {}.", adminPropertiesPath);
} catch (IOException | IllegalArgumentException e) {
LOG.warn("Failed to read administrative properties from {}. Returning empty properties.", adminPropertiesPath, e);
log("No admin properties found at {}.", List.of(adminPropertiesPath));
} catch (IOException | RuntimeException e) {
log("Failed to read administrative properties from {}. Returning empty properties.", List.of(adminPropertiesPath, e));
}
return adminProps;
return Map.of();
}
static void log(String message, List<Object> messageInput) {
BufferedLog.log(AdminPropertiesSetter.class.getName(), message, messageInput);
}
}

View File

@@ -0,0 +1,28 @@
package org.cryptomator.launcher;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.List;
class BufferedLog {
private final static List<Entry> logMessages = new ArrayList<>();
record Entry(String className, String message, List<Object> messageInput) {}
static void log(String className, String message, List<Object> messageInput) {
add(new BufferedLog.Entry(className, message, messageInput));
}
static void add(Entry e) {
logMessages.add(e);
}
public static void flush(Logger log) {
logMessages.forEach(e -> {
var message = "PRE LOG INIT Event in %s: %s".formatted(e.className, e.message);
log.info(message, e.messageInput.toArray());
});
}
}

View File

@@ -11,9 +11,9 @@ import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import org.cryptomator.common.ShutdownHook;
import org.cryptomator.common.SubstitutingProperties;
import org.cryptomator.networking.SSLContextProvider;
import org.cryptomator.ipc.IpcCommunicator;
import org.cryptomator.logging.DebugMode;
import org.cryptomator.networking.SSLContextProvider;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -27,7 +27,6 @@ import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.Executors;
@Singleton
@@ -66,6 +65,7 @@ public class Cryptomator {
}
public static void main(String[] args) {
BufferedLog.flush(LOG);
var printVersion = Optional.ofNullable(args) //
.stream() //Streams either one element (the args-array) or zero elements
.flatMap(Arrays::stream) //