From 900bbff5a7bb1f3fd3cbea3378c3e8ae56f21666 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sat, 25 Dec 2021 11:40:18 -0300 Subject: [PATCH 1/2] Upgrade to gradle 7.3.3 Upgrade checks to Log4j 2.17.0 See https://github.com/gradle/gradle/issues/19360 --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 234b6472e1..59250647c4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=2debee19271e1b82c6e41137d78e44e6e841035230a1a169ca47fd3fb09ed87b -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +distributionSha256Sum=b586e04868a22fd817c8971330fec37e298f3242eb85c374181b12d637f80302 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From aba58748458bbe8acb09f28bc8df987f2ca9b02f Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sat, 25 Dec 2021 13:24:52 -0300 Subject: [PATCH 2/2] Demo typesafe configuration library This is a simple example of how Misq might use the typesafe library (~295Kb) to load all global and sub-module configurations from a single misq.conf file. - Dependency constraint added to common-platform's build.gradle. - Dependency (implemention) declared in common and network build files. - Global misq.conf file added to common module's resource folder (might need to move it). This sample configuration contains a network service sub config, but only the "networkIOPool {}" block has real values. - Added new MisqConfig class to load misq.conf, and provide a getConfig(String path) method to give any object a typesafe Config instance of any scope: from global, to library, down to smallest grouping of scalar configuration values. - Added test to check "networkIOPool" config is loaded with correct values. - Replaced hard-coded NETWORK_IO_POOL parameters with values loaded from misq.conf TODO The typesafe library can probably support everying Misq will need in terms of configuration, including the ability to merge distinct dev and/or production configs (sub-module level), as well as property files and json files into a global config. --- common/build.gradle | 3 +- .../misq/common/configuration/MisqConfig.java | 87 +++++++++++++++++++ common/src/main/resources/misq.conf | 63 ++++++++++++++ network/build.gradle | 1 + .../network/misq/network/NetworkService.java | 12 ++- .../misq/network/TypesafeConfigTest.java | 23 +++++ platforms/common-platform/build.gradle | 7 ++ 7 files changed, 191 insertions(+), 5 deletions(-) create mode 100644 common/src/main/java/network/misq/common/configuration/MisqConfig.java create mode 100644 common/src/main/resources/misq.conf create mode 100644 network/src/test/java/network/misq/network/TypesafeConfigTest.java 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 . + */ +package network.misq.common.configuration; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import lombok.extern.slf4j.Slf4j; + +/** + * TIPS + *

+ * 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 e5c5e3be06..f686d7825c 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; @@ -47,6 +48,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 @@ -65,10 +67,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 /////////////////////////////////////////////////////////////////////////////////