Skip to content

Commit

Permalink
refactor(config): Dependency Inversion on ConfigManager and `C… (#491)
Browse files Browse the repository at this point in the history
Signed-off-by: Tobias Nett <[email protected]>
  • Loading branch information
skaldarnar authored Feb 19, 2020
1 parent ffdcc84 commit 83f68b4
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 52 deletions.
78 changes: 46 additions & 32 deletions src/main/java/org/terasology/launcher/config/ConfigManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ public class ConfigManager {
private static final String CONFIG_FILE = "config.json";
private static volatile ConfigManager instance;

private final Path launcherDir;
private final Path launcherPath;
private final Path configPath;
private final ConfigValidator validator;
private final Gson gson;
private Config config;
private final Service<Void> reader;
private final Service<Config> reader;
private final Service<Void> writer;

private ConfigManager() {
Expand All @@ -54,20 +55,26 @@ private ConfigManager() {
throw new RuntimeException("Cannot create second instance of a singleton class");
}

launcherDir = resolveLauncherDir();
configPath = launcherDir.resolve(CONFIG_FILE);
config = createDefaultConfig();
launcherPath = resolveLauncherDir();
configPath = launcherPath.resolve(CONFIG_FILE);
config = getDefaultConfigFor(launcherPath);
gson = new GsonBuilder()
.registerTypeAdapter(Path.class, new PathAdapter())
.registerTypeAdapter(Package.class, new PackageAdapter())
.disableHtmlEscaping()
.setPrettyPrinting()
.create();
validator = new ConfigValidator(config);

reader = new ConfigReader(this);
writer = new ConfigWriter(this);
reader = new ConfigReader(configPath, launcherPath, gson, validator);
writer = new ConfigWriter(configPath, gson, config);
}

/**
* Resolve the path to the launcher directory for host operating system.
*
* @return the absolute path to the launcher application directory
*/
private Path resolveLauncherDir() {
final OperatingSystem os = OperatingSystem.getOS();
if (os == OperatingSystem.UNKNOWN) {
Expand All @@ -82,19 +89,27 @@ private Path resolveLauncherDir() {
// TODO: Use local methods for all stuff above
}

private Config createDefaultConfig() {
/**
* The default configuration of the launcher with installation and data directories inside `launcherDir`.
*
* @param launcherDir the absolute path to launcher application directory
* @return the default configuration
*/
private Config getDefaultConfigFor(final Path launcherDir) {
final GameConfig gameConfig = GameConfig.builder()
.installDir(launcherDir.resolve("Terasology"))
.dataDir(launcherDir.resolve("TerasologyData"))
.maxMemory(JavaHeapSize.GB_1_5)
.initMemory(JavaHeapSize.GB_1)
.javaParam("-XX:+UseParNewGC"
+ " -XX:+UseConcMarkSweepGC"
+ " -XX:MaxGCPauseMillis=20"
+ " -XX:ParallelGCThreads=10")
.logLevel(LogLevel.DEFAULT)
.build();

return Config.builder()
.gameConfig(GameConfig.builder()
.installDir(launcherDir.resolve("Terasology"))
.dataDir(launcherDir.resolve("TerasologyData"))
.maxMemory(JavaHeapSize.GB_1_5)
.initMemory(JavaHeapSize.GB_1)
.javaParam("-XX:+UseParNewGC"
+ " -XX:+UseConcMarkSweepGC"
+ " -XX:MaxGCPauseMillis=20"
+ " -XX:ParallelGCThreads=10")
.logLevel(LogLevel.DEFAULT)
.build())
.gameConfig(gameConfig)
.locale(Locale.ENGLISH)
.launcherDir(launcherDir)
.checkUpdatesOnLaunch(false)
Expand All @@ -110,7 +125,7 @@ private Config createDefaultConfig() {
*
* @return the reader service
*/
public Service<Void> getReader() {
public Service<Config> getReader() {
return reader;
}

Expand All @@ -125,8 +140,8 @@ public Service<Void> getWriter() {
return writer;
}

Path getLauncherDir() {
return launcherDir;
Path getLauncherPath() {
return launcherPath;
}

Path getConfigPath() {
Expand All @@ -138,17 +153,20 @@ Gson getGson() {
}

/**
* Provides an immutable {@link Config} instance. It is
* initially filled with default configurations which
* are reset after reader service is run.
* Provides an immutable {@link Config} instance.
*
* @return the default {@link Config} instance
* The initial default configuration may be changed by running the reader service.
*
* @return the current launcher configuration as {@link Config} instance
*/
public Config getConfig() {
return config;
}

void setConfig(Config config) {
void setConfig(final Config config) {
if (config == null) {
throw new IllegalArgumentException("Configuration 'config' must not be 'null'!");
}
this.config = config;
}

Expand All @@ -160,11 +178,7 @@ void setConfig(Config config) {
public static ConfigManager get() {
// Double check locking
if (instance == null) {
synchronized (ConfigManager.class) {
if (instance == null) {
instance = new ConfigManager();
}
}
instance = new ConfigManager();
}
return instance;
}
Expand Down
28 changes: 16 additions & 12 deletions src/main/java/org/terasology/launcher/config/ConfigReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.terasology.launcher.config;

import com.google.gson.Gson;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import org.slf4j.Logger;
Expand All @@ -32,32 +33,35 @@
* On success the {@link Config} instance is validated
* and provided back to the {@link ConfigManager}.
*/
class ConfigReader extends Service<Void> {
class ConfigReader extends Service<Config> {
private static final Logger logger = LoggerFactory.getLogger(ConfigReader.class);

private final ConfigManager manager;
private final Path configPath;
private final Path launcherPath;
private final Gson decoder;
private final ConfigValidator validator;

ConfigReader(ConfigManager manager) {
this.manager = manager;
configPath = manager.getConfigPath();
validator = new ConfigValidator(manager.getConfig());
ConfigReader(final Path configPath, final Path launcherPath, final Gson decoder, final ConfigValidator validator) {
this.configPath = configPath;
this.launcherPath = launcherPath;
this.decoder = decoder;
this.validator = validator;
}

@Override
protected Task<Void> createTask() {
return new Task<Void>() {
protected Task<Config> createTask() {
return new Task<Config>() {
@Override
protected Void call() throws IOException {
protected Config call() throws IOException {
if (Files.exists(configPath)) {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(Files.newInputStream(configPath))
)) {
final Config config = manager.getGson().fromJson(reader, Config.Builder.class)
.launcherDir(manager.getLauncherDir())
final Config config = decoder.fromJson(reader, Config.Builder.class)
.launcherDir(launcherPath)
.build();
manager.setConfig(validator.validate(config));

return validator.validate(config);
}
} else {
logger.warn("Config file was not found: {}", configPath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
class ConfigValidator {
private static final Logger logger = LoggerFactory.getLogger(ConfigValidator.class);

private final Config defaultConfig;
private final Config fallbackConfiguration;

ConfigValidator(final Config defaultConfig) {
this.defaultConfig = defaultConfig;
this.fallbackConfiguration = defaultConfig;
}

/**
Expand Down Expand Up @@ -67,7 +67,7 @@ private JavaHeapSize validateMaxMemory(final GameConfig gameConfig) {
) {
valid = gameConfig.getMaxMemory();
} else {
valid = defaultConfig.getGameConfig().getMaxMemory();
valid = fallbackConfiguration.getGameConfig().getMaxMemory();
logger.warn("Max memory cannot be greater than 1.5 GB for a 32-bit JVM");
logger.debug("Continuing with max memory: {}", valid);
}
Expand Down
21 changes: 16 additions & 5 deletions src/main/java/org/terasology/launcher/config/ConfigWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.terasology.launcher.config;

import com.google.gson.Gson;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import org.slf4j.Logger;
Expand All @@ -34,12 +35,22 @@
class ConfigWriter extends Service<Void> {
private static final Logger logger = LoggerFactory.getLogger(ConfigWriter.class);

private final ConfigManager manager;
private final Path configPath;
private final Gson encoder;

ConfigWriter(ConfigManager manager) {
this.manager = manager;
configPath = manager.getConfigPath();
private Config config;

ConfigWriter(final Path configPath, final Gson encoder, final Config config) {
this.configPath = configPath;
this.encoder = encoder;
this.config = config;
}

public void setConfig(final Config config) {
if (config == null) {
throw new IllegalArgumentException("Configuration instance 'config' must not be 'null'!");
}
this.config = config;
}

@Override
Expand All @@ -50,7 +61,7 @@ protected Void call() throws IOException {
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(Files.newOutputStream(configPath))
)) {
manager.getGson().toJson(manager.getConfig(), Config.class, writer);
encoder.toJson(config, Config.class, writer);
}
return null;
}
Expand Down

0 comments on commit 83f68b4

Please sign in to comment.