diff --git a/common/build.gradle b/common/build.gradle
index 0c6e919406..dda9046270 100644
--- a/common/build.gradle
+++ b/common/build.gradle
@@ -13,8 +13,9 @@ apply from: '../buildSrc/lombok-dependencies.gradle'
dependencies {
api platform(project(':platforms:common-platform'))
-
+
implementation 'com.google.guava:guava'
+ implementation 'com.typesafe:config'
}
test {
diff --git a/common/src/main/java/network/misq/common/configuration/MisqConfig.java b/common/src/main/java/network/misq/common/configuration/MisqConfig.java
new file mode 100644
index 0000000000..e0ccf46ab3
--- /dev/null
+++ b/common/src/main/java/network/misq/common/configuration/MisqConfig.java
@@ -0,0 +1,87 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see
+ * Set the Java system property -Dconfig.trace=loads to get output on stderr describing each file that is loaded. + * (The output will show the library attempting to load misq.json, misq.properties, and reference.conf, but not fail.) + *
+ * Use myConfig.root().render() to get a Config as a string with comments showing where each value came from. + * This string can be printed out on console or logged to a file etc. + *
+ * If you see errors like com.typesafe.config.ConfigException$Missing: No configuration setting found for key foo, + * and you're sure that key is defined in your config file, they might appear e.g. when you're loading configuration + * from a thread that's not the JVM's main thread. Try passing the ClassLoader in manually - e.g. with + * ConfigFactory.load(getClass().getClassLoader()) or setting the context class loader. If you don't pass one, + * Lightbend Config uses the calling thread's contextClassLoader, and in some cases, it may not have your configuration + * files in its classpath, so loading the config on that thread can yield unexpected, erroneous results. + *
+ * REFERENCES + *
+ * https://github.com/lightbend/config + * https://www.stubbornjava.com/posts/typesafe-config-features-and-example-usage + * https://florentfo.rest/2019/01/07/configuring-spark-applications-with-typesafe-config.html + */ +@Slf4j +public class MisqConfig { + + public static final String NETWORK_CONFIG_PATH = "misq.networkConfig"; + public static final String NETWORK_IO_POOL_CONFIG_PATH = NETWORK_CONFIG_PATH + ".networkIOPool"; + + private static final Config MISQ_CONFIG = ConfigFactory.load("misq"); + + static { + try { + MISQ_CONFIG.checkValid(ConfigFactory.defaultReference(), "misq"); + } catch (Exception ex) { + throw new IllegalStateException("misq.conf validation failed", ex); + } + } + + /** + * Return the global Config object. + * + * @return Config + */ + public static Config getGlobalConfig() { + return MISQ_CONFIG; + } + + /** + * Return a Config for a given configuration path, e.g., + * "misq.networkConfig.torPeerGroupServiceConfig.peerExchangeConfig". + * + * @param path String representing a valid path in misq.conf + * @return Config + */ + public static Config getConfig(String path) { + MISQ_CONFIG.checkValid(ConfigFactory.defaultReference(), path); + return MISQ_CONFIG.getConfig(path); + } + + public static void main(String[] args) { + log.info("MISQ_CONFIG = {}", MISQ_CONFIG); + log.info("misq.networkConfig.torPeerGroupServiceConfig = {}", getConfig("misq.networkConfig.torPeerGroupServiceConfig")); + log.info("misq.networkConfig.i2pPeerGroupServiceConfig = {}", getConfig("misq.networkConfig.i2pPeerGroupServiceConfig")); + } +} diff --git a/common/src/main/resources/misq.conf b/common/src/main/resources/misq.conf new file mode 100644 index 0000000000..434303783e --- /dev/null +++ b/common/src/main/resources/misq.conf @@ -0,0 +1,63 @@ +// A comment +# Another comment +misq { + + networkConfig = { + + networkIOPool { + name = "NETWORK_IO_POOL" + corePoolSize = 1 + maximumPoolSize = 5000 + keepAliveTimeInSec = 10 + } + + baseDirPath = "some/path" + supportedTransportTypes = "TOR,I2P" + + transportConfig { + baseDirPath = "some/transport-config/path" + } + + serviceNodeConfig { + p2pServiceNodeConfig="PEER_GROUP,DATA,CONFIDENTIAL,RELAY,MONITOR" + } + + i2pPeerGroupServiceConfig { + peerGroupConfig { + minNumConnectedPeers=8 + maxNumConnectedPeers=12 + minNumReportedPeers=30 + } + + peerExchangeConfig { + numSeeNodesAtBoostrap=2 + numPersistedPeersAtBoostrap=100 + numReportedPeersAtBoostrap=50 + } + + keepAliveServiceConfig { + bootstrapTime=1111 + interval=2222 + } + } + + torPeerGroupServiceConfig { + peerGroupConfig { + minNumConnectedPeers=8 + maxNumConnectedPeers=12 + minNumReportedPeers=30 + } + + peerExchangeConfig { + numSeeNodesAtBoostrap=2 + numPersistedPeersAtBoostrap=100 + numReportedPeersAtBoostrap=50 + } + + keepAliveServiceConfig { + bootstrapTime=1111 + interval=2222 + } + } + } +} diff --git a/network/build.gradle b/network/build.gradle index db6ecbf7af..e8ff00aa9b 100644 --- a/network/build.gradle +++ b/network/build.gradle @@ -24,6 +24,7 @@ dependencies { implementation project(':security') implementation project(':persistence') + implementation 'com.typesafe:config' implementation 'com.google.guava:guava' implementation 'io.reactivex.rxjava2:rxjava' implementation 'com.google.protobuf:protobuf-gradle-plugin' diff --git a/network/src/main/java/network/misq/network/NetworkService.java b/network/src/main/java/network/misq/network/NetworkService.java index 93f477279e..96c9af2162 100644 --- a/network/src/main/java/network/misq/network/NetworkService.java +++ b/network/src/main/java/network/misq/network/NetworkService.java @@ -20,6 +20,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import network.misq.common.configuration.MisqConfig; import network.misq.common.threading.ExecutorFactory; import network.misq.common.util.NetworkUtils; import network.misq.network.http.HttpService; @@ -50,6 +51,7 @@ import java.util.function.BiConsumer; import static com.google.common.base.Preconditions.checkArgument; +import static network.misq.common.configuration.MisqConfig.NETWORK_IO_POOL_CONFIG_PATH; /** * High level API for network access to p2p network as well to http services (over Tor). If user has only I2P selected @@ -68,10 +70,12 @@ public class NetworkService { // If a user has 10 offers with dedicated nodes and 5 connections open, its another 100 threads + 50 at sending // messages. 100-200 threads might be a usual scenario, but it could also peak much higher, so we will give // maximumPoolSize sufficient headroom and use a rather short keepAliveTimeInSec. - public static final ThreadPoolExecutor NETWORK_IO_POOL = ExecutorFactory.getThreadPoolExecutor("NETWORK_IO_POOL", - 1, - 5000, - 10, + public static final com.typesafe.config.Config NETWORK_IO_POOL_CONFIG = MisqConfig.getConfig(NETWORK_IO_POOL_CONFIG_PATH); + public static final ThreadPoolExecutor NETWORK_IO_POOL = ExecutorFactory.getThreadPoolExecutor( + NETWORK_IO_POOL_CONFIG.getString("name"), + NETWORK_IO_POOL_CONFIG.getInt("corePoolSize"), + NETWORK_IO_POOL_CONFIG.getInt("maximumPoolSize"), + NETWORK_IO_POOL_CONFIG.getLong("keepAliveTimeInSec"), new SynchronousQueue<>()); public static record Config(String baseDirPath, diff --git a/network/src/test/java/network/misq/network/TypesafeConfigTest.java b/network/src/test/java/network/misq/network/TypesafeConfigTest.java new file mode 100644 index 0000000000..904202c56f --- /dev/null +++ b/network/src/test/java/network/misq/network/TypesafeConfigTest.java @@ -0,0 +1,23 @@ +package network.misq.network; + +import com.typesafe.config.Config; +import network.misq.common.configuration.MisqConfig; +import org.junit.jupiter.api.Test; + +import static network.misq.common.configuration.MisqConfig.NETWORK_IO_POOL_CONFIG_PATH; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TypesafeConfigTest { + + @Test + public void testNetworkPoolConfig() { + Config config = MisqConfig.getConfig(NETWORK_IO_POOL_CONFIG_PATH); + assertNotNull(config); + + assertEquals("NETWORK_IO_POOL", config.getString("name")); + assertEquals(1, config.getInt("corePoolSize")); + assertEquals(5000, config.getInt("maximumPoolSize")); + assertEquals(10, config.getLong("keepAliveTimeInSec")); + } +} diff --git a/platforms/common-platform/build.gradle b/platforms/common-platform/build.gradle index 168c4ae794..711c7ef1e7 100644 --- a/platforms/common-platform/build.gradle +++ b/platforms/common-platform/build.gradle @@ -12,6 +12,13 @@ dependencies { version { require '1.3.2' } } + ///////////////////////////////////////////////////////////////////////////////// + // Typesafe configuration dependency constraints + ///////////////////////////////////////////////////////////////////////////////// + api('com.typesafe:config') { + version { require '1.4.1' } + } + ///////////////////////////////////////////////////////////////////////////////// // Protobuf dependency constraints /////////////////////////////////////////////////////////////////////////////////