Archived
0

refactoring: реорганизация загрузки конфигураций

This commit is contained in:
2021-06-13 14:37:45 +03:00
parent e76f7ff375
commit 222f2dba61
10 changed files with 187 additions and 10 deletions

View File

@@ -38,8 +38,6 @@ dependencies {
testImplementation libs.test.junit5.api
testImplementation libs.test.junit5.params
testRuntimeOnly libs.test.junit5.engine
testRuntimeOnly libs.test.logger
}
test {

View File

@@ -23,8 +23,9 @@ application {
}
dependencies {
implementation project(':protocol-new')
implementation project(':cli-parser')
implementation libs.logger.logback
implementation libs.hocon
}

View File

@@ -0,0 +1,97 @@
package mc.server;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import com.typesafe.config.Config;
import lombok.SneakyThrows;
import mc.cliparser.CommandLine;
import mc.cliparser.CommandLineParser;
import mc.cliparser.Option;
import mc.server.di.ConfigComponent;
import mc.server.di.ConfigModule;
import mc.server.di.DaggerConfigComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
public class Main {
public static void main(String[] args) throws IOException {
//region Setup CommandLine
Option logconfigOption = Option.builder().longName("logconfig").hasArgs(true).build();
Option configOption = Option.builder().longName("config").hasArgs(true).build();
var cliParser = new CommandLineParser();
cliParser.addOption(logconfigOption);
cliParser.addOption(configOption);
CommandLine commandLine = cliParser.parse(args);
//endregion
//region Configuration Logback
Path logconfigPath;
if (commandLine.has(logconfigOption)) {
logconfigPath = Paths.get(logconfigOption.value());
} else {
logconfigPath = defaultLogbackConfigPath();
}
reconfigureLogback(logconfigPath);
//endregion
//region Setup config
ConfigModule configModule = new ConfigModule("./config-default.conf",
commandLine.has(configOption) ? Paths.get(configOption.value()) : null);
ConfigComponent configComponent = DaggerConfigComponent.builder()
.configModule(configModule).build();
Config config = configComponent.getConfig();
//endregion
Logger log = LoggerFactory.getLogger("LAUNCHER");
if (log.isDebugEnabled()) {
log.debug("Logback config path: {}", logconfigOption.value() == null ? "(default)" : logconfigOption.value());
log.debug("Config path: {}", configOption.value() == null ? "(default)" : configOption.value());
config.entrySet().stream()
.map(entry -> String.format("[CONFIG] %s = %s", entry.getKey(), entry.getValue().render()))
.sorted()
.forEach(log::debug);
}
}
/**
* Перенастраиваем logback с учетом путей.
* <p>По-умолчанию, logback пытается искать свои конфиги по заранее зашитым путям.
* Здесь мы изменяем эти принципы.
*/
private static void reconfigureLogback(@Nonnull Path configPath) throws IOException {
if (Files.notExists(configPath)) {
throw new FileNotFoundException(configPath.toAbsolutePath().toString());
}
LoggerContext logbackContext = (LoggerContext) LoggerFactory.getILoggerFactory();
logbackContext.reset();
JoranConfigurator configurator = new JoranConfigurator();
try(InputStream in = Files.newInputStream(configPath)) {
configurator.setContext(logbackContext);
configurator.doConfigure(in);
} catch (JoranException e) {
throw new IOException(e);
}
}
@SneakyThrows
private static Path defaultLogbackConfigPath() {
URL url = Objects.requireNonNull(Main.class.getResource("/logback-default.xml"));
return Paths.get(url.toURI());
}
}

View File

@@ -15,11 +15,22 @@ import java.nio.file.Path;
@Slf4j
public class ConfigModule {
private final String defaultResource;
private final Path configPath;
@Provides
@Singleton
Config provideConfig() {
return ConfigFactory.parseFile(configPath.toFile()).resolve();
Config defaultConfig = ConfigFactory.parseResources(defaultResource);
Config config;
if (configPath != null) {
Config userConfig = ConfigFactory.parseFile(configPath.toFile());
config = userConfig.withFallback(defaultConfig).resolve();
} else {
config = defaultConfig.resolve();
}
return config;
}
}

View File

@@ -0,0 +1,27 @@
server {
host: "127.0.0.1"
port: 25565
}
motd: """&bmc-project &8:: &4ZERO
&8develop by &7DmitriyMX"""
disconnect-reason: "&4Server is not available."
players {
max-online: 0
fake-online {
enable: false
value: 0
}
}
# Размер значка: 64x64 px
icon {
enable: false
path: "favicon.png"
}
world {
view-distance: 1
}

View File

@@ -12,9 +12,12 @@
<appender-ref ref="CONSOLE"/>
</root>
<!-- раскоментировать для простотра дампа пакетов
<logger name="LAUNCHER" level="debug" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
<!-- раскоментировать для простотра дампа пакетов -->
<logger name="io.netty.handler.logging.LoggingHandler" level="debug" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
-->
</configuration>

View File

@@ -12,10 +12,15 @@ import static org.junit.jupiter.api.Assertions.*;
class ConfigModuleTest {
private static final String emptyConfig = "./config-empty.conf";
/*
Проверяем, что загруженный объект конфига является singleton
*/
@Test
void singleton() {
ConfigComponent component = DaggerConfigComponent.builder()
.configModule(new ConfigModule(pathResource("/config-1.conf"))).build();
.configModule(new ConfigModule(emptyConfig, pathResource("/config-1.conf"))).build();
Config config1 = component.getConfig();
Config config2 = component.getConfig();
@@ -23,10 +28,13 @@ class ConfigModuleTest {
assertSame(config1, config2);
}
/*
Корректная загрузка конфига
*/
@Test
void loadConfig() {
ConfigComponent component = DaggerConfigComponent.builder()
.configModule(new ConfigModule(pathResource("/config-1.conf"))).build();
.configModule(new ConfigModule(emptyConfig, pathResource("/config-1.conf"))).build();
Config config = component.getConfig();
assertEquals("value1", config.getString("key1"));
@@ -36,10 +44,13 @@ class ConfigModuleTest {
assertEquals("value5", config.getString("key5"));
}
/*
Проверка include
*/
@Test
void includeTest() {
ConfigComponent component = DaggerConfigComponent.builder()
.configModule(new ConfigModule(pathResource("/config-2.conf"))).build();
.configModule(new ConfigModule(emptyConfig, pathResource("/config-2.conf"))).build();
Config config = component.getConfig();
assertEquals("value1", config.getString("key1"));
@@ -49,6 +60,31 @@ class ConfigModuleTest {
assertEquals("value5", config.getString("key5"));
}
/*
Работа с многострочностью
*/
@Test
void multilineTest() {
ConfigComponent component = DaggerConfigComponent.builder()
.configModule(new ConfigModule(emptyConfig, pathResource("/config-1.conf"))).build();
Config config = component.getConfig();
assertEquals("line1\nline2", config.getString("key6"));
}
/*
Проверяем работу merge config
*/
@Test
void mergeConfigTest() {
ConfigComponent component = DaggerConfigComponent.builder()
.configModule(new ConfigModule("./config-1.conf", pathResource("/config-3.conf"))).build();
Config config = component.getConfig();
assertEquals("value1_merged", config.getString("key1"));
assertEquals("value2", config.getString("key2.subkey1"));
}
@SneakyThrows
private static Path pathResource(String resource) {
URL url = ConfigModuleTest.class.getResource(resource);

View File

@@ -9,4 +9,7 @@ key3 {
"key4.somename": value4
variable: value5
key5: ${variable}
key5: ${variable}
key6: """line1
line2"""

View File

@@ -0,0 +1 @@
key1: value1_merged