mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-19 03:01:27 +00:00
Refactor properties preprocessing:
* decorate Properties class * set the system properties to decorator * for logging setup, skip enviroment and access props over decorator
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
package org.cryptomator.common;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.InvalidPropertiesFormatException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class LazyProcessedProperties extends Properties {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LazyProcessedProperties.class);
|
||||
|
||||
//Template and env _need_ to be instance variables, otherwise they might not be initialized at access time
|
||||
private final Pattern template = Pattern.compile("@\\{(\\w+)}");
|
||||
private final Map<String, String> env = System.getenv();
|
||||
private final Properties delegate;
|
||||
|
||||
public LazyProcessedProperties(Properties props) {
|
||||
this.delegate = props;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String key) {
|
||||
var value = delegate.getProperty(key);
|
||||
if (key.startsWith("cryptomator.") && value != null) {
|
||||
return process(value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String key, String defaultValue) {
|
||||
var value = delegate.getProperty(key, defaultValue);
|
||||
if (key.startsWith("cryptomator.") && value != null) {
|
||||
return process(value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
String process(String value) {
|
||||
return template.matcher(value).replaceAll(match -> //
|
||||
switch (match.group(1)) {
|
||||
case "appdir" -> resolveFrom("APPDIR", Source.ENV);
|
||||
case "appdata" -> resolveFrom("APPDATA", Source.ENV);
|
||||
case "localappdata" -> resolveFrom("LOCALAPPDATA", Source.ENV);
|
||||
case "userhome" -> resolveFrom("user.home", Source.PROPS);
|
||||
default -> {
|
||||
LOG.warn("Unknown variable @{{}} in property value {}.", match.group(), value);
|
||||
yield match.group();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String resolveFrom(String key, Source src) {
|
||||
var val = switch (src) {
|
||||
case ENV -> env.get(key);
|
||||
case PROPS -> delegate.getProperty(key);
|
||||
};
|
||||
if (val == null) {
|
||||
LOG.warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src);
|
||||
return "";
|
||||
} else {
|
||||
return val.replace("\\", "\\\\");
|
||||
}
|
||||
}
|
||||
|
||||
private enum Source {
|
||||
ENV,
|
||||
PROPS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setProperty(String key, String value) {
|
||||
return delegate.setProperty(key, value);
|
||||
}
|
||||
|
||||
//auto generated
|
||||
@Override
|
||||
public void load(Reader reader) throws IOException {delegate.load(reader);}
|
||||
|
||||
@Override
|
||||
public void load(InputStream inStream) throws IOException {delegate.load(inStream);}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void save(OutputStream out, String comments) {delegate.save(out, comments);}
|
||||
|
||||
@Override
|
||||
public void store(Writer writer, String comments) throws IOException {delegate.store(writer, comments);}
|
||||
|
||||
@Override
|
||||
public void store(OutputStream out, @Nullable String comments) throws IOException {delegate.store(out, comments);}
|
||||
|
||||
@Override
|
||||
public void loadFromXML(InputStream in) throws IOException, InvalidPropertiesFormatException {delegate.loadFromXML(in);}
|
||||
|
||||
@Override
|
||||
public void storeToXML(OutputStream os, String comment) throws IOException {delegate.storeToXML(os, comment);}
|
||||
|
||||
@Override
|
||||
public void storeToXML(OutputStream os, String comment, String encoding) throws IOException {delegate.storeToXML(os, comment, encoding);}
|
||||
|
||||
@Override
|
||||
public void storeToXML(OutputStream os, String comment, Charset charset) throws IOException {delegate.storeToXML(os, comment, charset);}
|
||||
|
||||
@Override
|
||||
public Enumeration<?> propertyNames() {return delegate.propertyNames();}
|
||||
|
||||
@Override
|
||||
public Set<String> stringPropertyNames() {return delegate.stringPropertyNames();}
|
||||
|
||||
@Override
|
||||
public void list(PrintStream out) {delegate.list(out);}
|
||||
|
||||
@Override
|
||||
public void list(PrintWriter out) {delegate.list(out);}
|
||||
|
||||
@Override
|
||||
public int size() {return delegate.size();}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {return delegate.isEmpty();}
|
||||
|
||||
@Override
|
||||
public Enumeration<Object> keys() {return delegate.keys();}
|
||||
|
||||
@Override
|
||||
public Enumeration<Object> elements() {return delegate.elements();}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object value) {return delegate.contains(value);}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {return delegate.containsValue(value);}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {return delegate.containsKey(key);}
|
||||
|
||||
@Override
|
||||
public Object get(Object key) {return delegate.get(key);}
|
||||
|
||||
@Override
|
||||
public Object put(Object key, Object value) {return delegate.put(key, value);}
|
||||
|
||||
@Override
|
||||
public Object remove(Object key) {return delegate.remove(key);}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<?, ?> t) {delegate.putAll(t);}
|
||||
|
||||
@Override
|
||||
public void clear() {delegate.clear();}
|
||||
|
||||
@Override
|
||||
public String toString() {return delegate.toString();}
|
||||
|
||||
@Override
|
||||
public Set<Object> keySet() {return delegate.keySet();}
|
||||
|
||||
@Override
|
||||
public Collection<Object> values() {return delegate.values();}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<Object, Object>> entrySet() {return delegate.entrySet();}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {return delegate.equals(o);}
|
||||
|
||||
@Override
|
||||
public int hashCode() {return delegate.hashCode();}
|
||||
|
||||
@Override
|
||||
public Object getOrDefault(Object key, Object defaultValue) {return delegate.getOrDefault(key, defaultValue);}
|
||||
|
||||
@Override
|
||||
public void forEach(BiConsumer<? super Object, ? super Object> action) {delegate.forEach(action);}
|
||||
|
||||
@Override
|
||||
public void replaceAll(BiFunction<? super Object, ? super Object, ?> function) {delegate.replaceAll(function);}
|
||||
|
||||
@Override
|
||||
public Object putIfAbsent(Object key, Object value) {return delegate.putIfAbsent(key, value);}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object key, Object value) {return delegate.remove(key, value);}
|
||||
|
||||
@Override
|
||||
public boolean replace(Object key, Object oldValue, Object newValue) {return delegate.replace(key, oldValue, newValue);}
|
||||
|
||||
@Override
|
||||
public Object replace(Object key, Object value) {return delegate.replace(key, value);}
|
||||
|
||||
@Override
|
||||
public Object computeIfAbsent(Object key, Function<? super Object, ?> mappingFunction) {return delegate.computeIfAbsent(key, mappingFunction);}
|
||||
|
||||
@Override
|
||||
public Object computeIfPresent(Object key, BiFunction<? super Object, ? super Object, ?> remappingFunction) {return delegate.computeIfPresent(key, remappingFunction);}
|
||||
|
||||
@Override
|
||||
public Object compute(Object key, BiFunction<? super Object, ? super Object, ?> remappingFunction) {return delegate.compute(key, remappingFunction);}
|
||||
|
||||
@Override
|
||||
public Object merge(Object key, Object value, BiFunction<? super Object, ? super Object, ?> remappingFunction) {return delegate.merge(key, value, remappingFunction);}
|
||||
|
||||
@Override
|
||||
public Object clone() {return delegate.clone();}
|
||||
|
||||
public static <K, V> Map<K, V> of() {return Map.of();}
|
||||
|
||||
public static <K, V> Map<K, V> of(K k1, V v1) {return Map.of(k1, v1);}
|
||||
|
||||
public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2) {return Map.of(k1, v1, k2, v2);}
|
||||
|
||||
public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) {return Map.of(k1, v1, k2, v2, k3, v3);}
|
||||
|
||||
public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {return Map.of(k1, v1, k2, v2, k3, v3, k4, v4);}
|
||||
|
||||
public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {return Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5);}
|
||||
|
||||
public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) {return Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6);}
|
||||
|
||||
public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) {return Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7);}
|
||||
|
||||
public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8) {return Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8);}
|
||||
|
||||
public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9) {return Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9);}
|
||||
|
||||
public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) {return Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10);}
|
||||
|
||||
@SafeVarargs
|
||||
public static <K, V> Map<K, V> ofEntries(Map.Entry<? extends K, ? extends V>... entries) {return Map.ofEntries(entries);}
|
||||
|
||||
public static <K, V> Map.Entry<K, V> entry(K k, V v) {return Map.entry(k, v);}
|
||||
|
||||
public static <K, V> Map<K, V> copyOf(Map<? extends K, ? extends V> map) {return Map.copyOf(map);}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package org.cryptomator.common;
|
||||
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class PropertiesPreprocessor {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PropertiesPreprocessor.class);
|
||||
private static final Pattern TEMPLATE = Pattern.compile("@\\{(\\w+)}");
|
||||
private static final LoggingEnvironment ENV = new LoggingEnvironment(System.getenv(), LOG);
|
||||
|
||||
public static void run() {
|
||||
var properties = System.getProperties();
|
||||
properties.stringPropertyNames().stream() //
|
||||
.filter(s -> s.startsWith("cryptomator.")) //
|
||||
.forEach(key -> {
|
||||
var value = properties.getProperty(key);
|
||||
var newValue = process(value);
|
||||
if(! value.equals(newValue)) {
|
||||
LOG.info("Changed property {} from {} to {}.", key, value, newValue);
|
||||
}
|
||||
properties.setProperty(key, newValue);
|
||||
});
|
||||
LOG.info("Preprocessed cryptomator properties.");
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static String process(String value) {
|
||||
return TEMPLATE.matcher(value).replaceAll(match -> //
|
||||
switch (match.group(1)) {
|
||||
case "appdir" -> ENV.get("APPDIR");
|
||||
case "appdata" -> ENV.get("APPDATA");
|
||||
case "localappdata" -> ENV.get("LOCALAPPDATA");
|
||||
case "userhome" -> System.getProperty("user.home");
|
||||
default -> {
|
||||
LOG.warn("Found unknown variable @{{}} in property value {}.", match.group(), value);
|
||||
yield match.group();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class LoggingEnvironment {
|
||||
|
||||
private final Map<String, String> env;
|
||||
private final Logger logger;
|
||||
|
||||
LoggingEnvironment(Map<String, String> env, Logger logger) {
|
||||
this.env = env;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
String get(String key) {
|
||||
var val = env.get(key);
|
||||
if (val == null) {
|
||||
logger.warn("Variable {} used for substitution not found in environment", key);
|
||||
return "";
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import dagger.Lazy;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.PropertiesPreprocessor;
|
||||
import org.cryptomator.common.LazyProcessedProperties;
|
||||
import org.cryptomator.common.ShutdownHook;
|
||||
import org.cryptomator.ipc.IpcCommunicator;
|
||||
import org.cryptomator.logging.DebugMode;
|
||||
@@ -32,8 +32,16 @@ public class Cryptomator {
|
||||
private static final long STARTUP_TIME = System.currentTimeMillis();
|
||||
// DaggerCryptomatorComponent gets generated by Dagger.
|
||||
// Run Maven and include target/generated-sources/annotations in your IDE.
|
||||
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
|
||||
|
||||
static {
|
||||
var lazyProcessedProps = new LazyProcessedProperties(System.getProperties());
|
||||
System.setProperties(lazyProcessedProps);
|
||||
CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
|
||||
LOG = LoggerFactory.getLogger(Cryptomator.class);
|
||||
}
|
||||
|
||||
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT;
|
||||
private static final Logger LOG;
|
||||
|
||||
private final DebugMode debugMode;
|
||||
private final SupportedLanguages supportedLanguages;
|
||||
@@ -64,7 +72,6 @@ public class Cryptomator {
|
||||
System.out.printf("Cryptomator version %s (build %s)%n", appVer, buildNumber);
|
||||
return;
|
||||
}
|
||||
PropertiesPreprocessor.run();
|
||||
int exitCode = CRYPTOMATOR_COMPONENT.application().run(args);
|
||||
LOG.info("Exit {}", exitCode);
|
||||
System.exit(exitCode); // end remaining non-daemon threads.
|
||||
|
||||
@@ -14,10 +14,12 @@ import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
|
||||
import ch.qos.logback.core.rolling.RollingFileAppender;
|
||||
import ch.qos.logback.core.spi.ContextAwareBase;
|
||||
import ch.qos.logback.core.util.FileSize;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.LazyProcessedProperties;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class LogbackConfigurator extends ContextAwareBase implements Configurator {
|
||||
|
||||
@@ -56,8 +58,10 @@ public class LogbackConfigurator extends ContextAwareBase implements Configurato
|
||||
|
||||
@Override
|
||||
public ExecutionStatus configure(LoggerContext context) {
|
||||
var useCustomCfg = Environment.getInstance().useCustomLogbackConfig();
|
||||
var logDir = Environment.getInstance().getLogDir().orElse(null);
|
||||
//we need to preprocess those, because every other class has a dependency to logging, none are initialized yet
|
||||
var processedProps = new LazyProcessedProperties(System.getProperties());
|
||||
var useCustomCfg = Optional.ofNullable(processedProps.getProperty("logback.configurationFile")).map(s -> Files.exists(Path.of(s))).orElse(false);
|
||||
var logDir = Optional.ofNullable(processedProps.getProperty("cryptomator.logDir")).map(Path::of).orElse(null);
|
||||
|
||||
if (useCustomCfg) {
|
||||
addInfo("Using external logback configuration file.");
|
||||
|
||||
Reference in New Issue
Block a user