refactoring: реорганизация загрузки конфигураций
This commit is contained in:
@@ -38,8 +38,6 @@ dependencies {
|
|||||||
testImplementation libs.test.junit5.api
|
testImplementation libs.test.junit5.api
|
||||||
testImplementation libs.test.junit5.params
|
testImplementation libs.test.junit5.params
|
||||||
testRuntimeOnly libs.test.junit5.engine
|
testRuntimeOnly libs.test.junit5.engine
|
||||||
|
|
||||||
testRuntimeOnly libs.test.logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':protocol-new')
|
implementation project(':cli-parser')
|
||||||
|
|
||||||
|
implementation libs.logger.logback
|
||||||
implementation libs.hocon
|
implementation libs.hocon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
97
server-new/src/main/java/mc/server/Main.java
Normal file
97
server-new/src/main/java/mc/server/Main.java
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,11 +15,22 @@ import java.nio.file.Path;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class ConfigModule {
|
public class ConfigModule {
|
||||||
|
|
||||||
|
private final String defaultResource;
|
||||||
private final Path configPath;
|
private final Path configPath;
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
Config provideConfig() {
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
server-new/src/main/resources/config-default.conf
Normal file
27
server-new/src/main/resources/config-default.conf
Normal 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
|
||||||
|
}
|
||||||
@@ -12,9 +12,12 @@
|
|||||||
<appender-ref ref="CONSOLE"/>
|
<appender-ref ref="CONSOLE"/>
|
||||||
</root>
|
</root>
|
||||||
|
|
||||||
<!-- раскоментировать для простотра дампа пакетов
|
<logger name="LAUNCHER" level="debug" additivity="false">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<!-- раскоментировать для простотра дампа пакетов -->
|
||||||
<logger name="io.netty.handler.logging.LoggingHandler" level="debug" additivity="false">
|
<logger name="io.netty.handler.logging.LoggingHandler" level="debug" additivity="false">
|
||||||
<appender-ref ref="CONSOLE"/>
|
<appender-ref ref="CONSOLE"/>
|
||||||
</logger>
|
</logger>
|
||||||
-->
|
|
||||||
</configuration>
|
</configuration>
|
||||||
@@ -12,10 +12,15 @@ import static org.junit.jupiter.api.Assertions.*;
|
|||||||
|
|
||||||
class ConfigModuleTest {
|
class ConfigModuleTest {
|
||||||
|
|
||||||
|
private static final String emptyConfig = "./config-empty.conf";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Проверяем, что загруженный объект конфига является singleton
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
void singleton() {
|
void singleton() {
|
||||||
ConfigComponent component = DaggerConfigComponent.builder()
|
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 config1 = component.getConfig();
|
||||||
Config config2 = component.getConfig();
|
Config config2 = component.getConfig();
|
||||||
|
|
||||||
@@ -23,10 +28,13 @@ class ConfigModuleTest {
|
|||||||
assertSame(config1, config2);
|
assertSame(config1, config2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Корректная загрузка конфига
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
void loadConfig() {
|
void loadConfig() {
|
||||||
ConfigComponent component = DaggerConfigComponent.builder()
|
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();
|
Config config = component.getConfig();
|
||||||
assertEquals("value1", config.getString("key1"));
|
assertEquals("value1", config.getString("key1"));
|
||||||
@@ -36,10 +44,13 @@ class ConfigModuleTest {
|
|||||||
assertEquals("value5", config.getString("key5"));
|
assertEquals("value5", config.getString("key5"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Проверка include
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
void includeTest() {
|
void includeTest() {
|
||||||
ConfigComponent component = DaggerConfigComponent.builder()
|
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();
|
Config config = component.getConfig();
|
||||||
assertEquals("value1", config.getString("key1"));
|
assertEquals("value1", config.getString("key1"));
|
||||||
@@ -49,6 +60,31 @@ class ConfigModuleTest {
|
|||||||
assertEquals("value5", config.getString("key5"));
|
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
|
@SneakyThrows
|
||||||
private static Path pathResource(String resource) {
|
private static Path pathResource(String resource) {
|
||||||
URL url = ConfigModuleTest.class.getResource(resource);
|
URL url = ConfigModuleTest.class.getResource(resource);
|
||||||
|
|||||||
@@ -10,3 +10,6 @@ key3 {
|
|||||||
|
|
||||||
variable: value5
|
variable: value5
|
||||||
key5: ${variable}
|
key5: ${variable}
|
||||||
|
|
||||||
|
key6: """line1
|
||||||
|
line2"""
|
||||||
1
server-new/src/test/resources/config-3.conf
Normal file
1
server-new/src/test/resources/config-3.conf
Normal file
@@ -0,0 +1 @@
|
|||||||
|
key1: value1_merged
|
||||||
0
server-new/src/test/resources/config-empty.conf
Normal file
0
server-new/src/test/resources/config-empty.conf
Normal file
Reference in New Issue
Block a user