Split out NodeConfiguration helpers into a separate utility class

This commit is contained in:
Andrius Dagys 2016-10-06 17:21:18 +01:00
parent 0bcecac7f6
commit 4e2f0e0ff9
11 changed files with 136 additions and 133 deletions

View File

@ -1,7 +1,7 @@
package com.r3corda.node
import com.r3corda.node.services.config.ConfigHelper
import com.r3corda.node.services.config.FullNodeConfiguration
import com.r3corda.node.services.config.NodeConfiguration
import joptsimple.OptionParser
import org.slf4j.LoggerFactory
import java.lang.management.ManagementFactory
@ -36,7 +36,7 @@ fun main(args: Array<String>) {
val baseDirectoryPath = if (cmdlineOptions.has(ParamsSpec.baseDirectoryArg)) Paths.get(cmdlineOptions.valueOf(ParamsSpec.baseDirectoryArg)) else Paths.get(".").normalize()
val configFile = if (cmdlineOptions.has(ParamsSpec.configFileArg)) Paths.get(cmdlineOptions.valueOf(ParamsSpec.configFileArg)) else null
val conf = FullNodeConfiguration(NodeConfiguration.loadConfig(baseDirectoryPath, configFile))
val conf = FullNodeConfiguration(ConfigHelper.loadConfig(baseDirectoryPath, configFile))
val dir = conf.basedir.toAbsolutePath().normalize()
logInfo(args, dir)

View File

@ -7,8 +7,8 @@ import com.r3corda.core.crypto.generateKeyPair
import com.r3corda.core.node.NodeInfo
import com.r3corda.core.node.services.NetworkMapCache
import com.r3corda.core.node.services.ServiceInfo
import com.r3corda.node.services.config.ConfigHelper
import com.r3corda.node.services.config.FullNodeConfiguration
import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.messaging.ArtemisMessagingComponent
import com.r3corda.node.services.messaging.ArtemisMessagingServer
import com.r3corda.node.services.messaging.NodeMessagingClient
@ -292,7 +292,7 @@ class DriverDSL(
val nodeDirectory = "$baseDirectory/$name"
val config = NodeConfiguration.loadConfig(
val config = ConfigHelper.loadConfig(
baseDirectoryPath = Paths.get(nodeDirectory),
allowMissingConfig = true,
configOverrides = mapOf(
@ -324,7 +324,7 @@ class DriverDSL(
): Future<NodeMessagingClient> {
val nodeConfiguration = FullNodeConfiguration(
NodeConfiguration.loadConfig(
ConfigHelper.loadConfig(
baseDirectoryPath = Paths.get(baseDirectory, providedName),
allowMissingConfig = true,
configOverrides = mapOf(
@ -353,7 +353,7 @@ class DriverDSL(
override fun startLocalServer(): Future<ArtemisMessagingServer> {
val name = "driver-local-server"
val config = FullNodeConfiguration(
NodeConfiguration.loadConfig(
ConfigHelper.loadConfig(
baseDirectoryPath = Paths.get(baseDirectory, name),
allowMissingConfig = true,
configOverrides = mapOf(
@ -397,7 +397,7 @@ class DriverDSL(
val nodeDirectory = "$baseDirectory/$networkMapName"
val config = NodeConfiguration.loadConfig(
val config = ConfigHelper.loadConfig(
baseDirectoryPath = Paths.get(nodeDirectory),
allowMissingConfig = true,
configOverrides = mapOf(

View File

@ -25,6 +25,7 @@ import com.r3corda.core.utilities.debug
import com.r3corda.node.api.APIServer
import com.r3corda.node.services.api.*
import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.config.configureWithDevSSLCertificate
import com.r3corda.node.services.events.NodeSchedulerService
import com.r3corda.node.services.events.ScheduledActivityObserver
import com.r3corda.node.services.identity.InMemoryIdentityService

View File

@ -0,0 +1,102 @@
package com.r3corda.node.services.config
import com.google.common.net.HostAndPort
import com.r3corda.core.crypto.X509Utilities
import com.r3corda.core.utilities.loggerFor
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
import com.typesafe.config.ConfigRenderOptions
import java.net.URL
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.time.Instant
import java.time.LocalDate
import java.util.*
import kotlin.reflect.KProperty
import kotlin.reflect.jvm.javaType
object ConfigHelper {
val log = loggerFor<ConfigHelper>()
fun loadConfig(baseDirectoryPath: Path,
configFileOverride: Path? = null,
allowMissingConfig: Boolean = false,
configOverrides: Map<String, Any?> = emptyMap()): Config {
val defaultConfig = ConfigFactory.parseResources("reference.conf", ConfigParseOptions.defaults().setAllowMissing(false))
val normalisedBaseDir = baseDirectoryPath.normalize()
val configFile = (configFileOverride?.normalize() ?: normalisedBaseDir.resolve("node.conf")).toFile()
val appConfig = ConfigFactory.parseFile(configFile, ConfigParseOptions.defaults().setAllowMissing(allowMissingConfig))
val overridesMap = HashMap<String, Any?>() // If we do require a few other command line overrides eg for a nicer development experience they would go inside this map.
overridesMap.putAll(configOverrides)
overridesMap["basedir"] = normalisedBaseDir.toAbsolutePath().toString()
val overrideConfig = ConfigFactory.parseMap(overridesMap)
val mergedAndResolvedConfig = overrideConfig.withFallback(appConfig).withFallback(defaultConfig).resolve()
log.info("Config:\n ${mergedAndResolvedConfig.root().render(ConfigRenderOptions.defaults())}")
return mergedAndResolvedConfig
}
}
@Suppress("UNCHECKED_CAST")
operator fun <T> Config.getValue(receiver: Any, metadata: KProperty<*>): T {
return when (metadata.returnType.javaType) {
String::class.java -> getString(metadata.name) as T
Int::class.java -> getInt(metadata.name) as T
Long::class.java -> getLong(metadata.name) as T
Double::class.java -> getDouble(metadata.name) as T
Boolean::class.java -> getBoolean(metadata.name) as T
LocalDate::class.java -> LocalDate.parse(getString(metadata.name)) as T
Instant::class.java -> Instant.parse(getString(metadata.name)) as T
HostAndPort::class.java -> HostAndPort.fromString(getString(metadata.name)) as T
Path::class.java -> Paths.get(getString(metadata.name)) as T
URL::class.java -> URL(getString(metadata.name)) as T
Properties::class.java -> getProperties(metadata.name) as T
else -> throw IllegalArgumentException("Unsupported type ${metadata.returnType}")
}
}
/**
* Helper class for optional configurations
*/
class OptionalConfig<out T>(val conf: Config, val lambda: () -> T) {
operator fun getValue(receiver: Any, metadata: KProperty<*>): T {
return if (conf.hasPath(metadata.name)) conf.getValue(receiver, metadata) else lambda()
}
}
fun <T> Config.getOrElse(lambda: () -> T): OptionalConfig<T> {
return OptionalConfig(this, lambda)
}
fun Config.getProperties(path: String): Properties {
val obj = this.getObject(path)
val props = Properties()
for ((property, objectValue) in obj.entries) {
props.setProperty(property, objectValue.unwrapped().toString())
}
return props
}
/**
* Strictly for dev only automatically construct a server certificate/private key signed from
* the CA certs in Node resources. Then provision KeyStores into certificates folder under node path.
*/
fun NodeSSLConfiguration.configureWithDevSSLCertificate() {
Files.createDirectories(certificatesPath)
if (!Files.exists(trustStorePath)) {
Files.copy(javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordatruststore.jks"),
trustStorePath)
}
if (!Files.exists(keyStorePath)) {
val caKeyStore = X509Utilities.loadKeyStore(
javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordadevcakeys.jks"),
"cordacadevpass")
X509Utilities.createKeystoreForSSL(keyStorePath, keyStorePassword, keyStorePassword, caKeyStore, "cordacadevkeypass")
}
}

View File

@ -1,7 +1,6 @@
package com.r3corda.node.services.config
import com.google.common.net.HostAndPort
import com.r3corda.core.crypto.X509Utilities
import com.r3corda.core.div
import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.core.node.services.ServiceInfo
@ -9,19 +8,8 @@ import com.r3corda.node.internal.Node
import com.r3corda.node.services.messaging.NodeMessagingClient
import com.r3corda.node.services.network.NetworkMapService
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
import com.typesafe.config.ConfigRenderOptions
import org.slf4j.LoggerFactory
import java.net.URL
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.time.Instant
import java.time.LocalDate
import java.util.*
import kotlin.reflect.KProperty
import kotlin.reflect.jvm.javaType
interface NodeSSLConfiguration {
val keyStorePassword: String
@ -29,24 +17,6 @@ interface NodeSSLConfiguration {
val certificatesPath: Path
val keyStorePath: Path get() = certificatesPath / "sslkeystore.jks"
val trustStorePath: Path get() = certificatesPath / "truststore.jks"
/**
* Strictly for dev only automatically construct a server certificate/private key signed from
* the CA certs in Node resources. Then provision KeyStores into certificates folder under node path.
*/
fun configureWithDevSSLCertificate() {
Files.createDirectories(certificatesPath)
if (!Files.exists(trustStorePath)) {
Files.copy(javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordatruststore.jks"),
trustStorePath)
}
if (!Files.exists(keyStorePath)) {
val caKeyStore = X509Utilities.loadKeyStore(
javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordadevcakeys.jks"),
"cordacadevpass")
X509Utilities.createKeystoreForSSL(keyStorePath, keyStorePassword, keyStorePassword, caKeyStore, "cordacadevkeypass")
}
}
}
interface NodeConfiguration : NodeSSLConfiguration {
@ -58,85 +28,24 @@ interface NodeConfiguration : NodeSSLConfiguration {
val exportJMXto: String
val dataSourceProperties: Properties get() = Properties()
val devMode: Boolean
companion object {
val log = LoggerFactory.getLogger("NodeConfiguration")
fun loadConfig(baseDirectoryPath: Path, configFileOverride: Path? = null, allowMissingConfig: Boolean = false, configOverrides: Map<String, Any?> = emptyMap()): Config {
val defaultConfig = ConfigFactory.parseResources("reference.conf", ConfigParseOptions.defaults().setAllowMissing(false))
val normalisedBaseDir = baseDirectoryPath.normalize()
val configFile = (configFileOverride?.normalize() ?: normalisedBaseDir.resolve("node.conf")).toFile()
val appConfig = ConfigFactory.parseFile(configFile, ConfigParseOptions.defaults().setAllowMissing(allowMissingConfig))
val overridesMap = HashMap<String, Any?>() // If we do require a few other command line overrides eg for a nicer development experience they would go inside this map.
overridesMap.putAll(configOverrides)
overridesMap["basedir"] = normalisedBaseDir.toAbsolutePath().toString()
val overrideConfig = ConfigFactory.parseMap(overridesMap)
val mergedAndResolvedConfig = overrideConfig.withFallback(appConfig).withFallback(defaultConfig).resolve()
log.info("config:\n ${mergedAndResolvedConfig.root().render(ConfigRenderOptions.defaults())}")
return mergedAndResolvedConfig
}
}
}
@Suppress("UNCHECKED_CAST")
operator fun <T> Config.getValue(receiver: Any, metadata: KProperty<*>): T {
return when (metadata.returnType.javaType) {
String::class.java -> getString(metadata.name) as T
Int::class.java -> getInt(metadata.name) as T
Long::class.java -> getLong(metadata.name) as T
Double::class.java -> getDouble(metadata.name) as T
Boolean::class.java -> getBoolean(metadata.name) as T
LocalDate::class.java -> LocalDate.parse(getString(metadata.name)) as T
Instant::class.java -> Instant.parse(getString(metadata.name)) as T
HostAndPort::class.java -> HostAndPort.fromString(getString(metadata.name)) as T
Path::class.java -> Paths.get(getString(metadata.name)) as T
URL::class.java -> URL(getString(metadata.name)) as T
Properties::class.java -> getProperties(metadata.name) as T
else -> throw IllegalArgumentException("Unsupported type ${metadata.returnType}")
}
}
/**
* Helper class for optional configurations
*/
class OptionalConfig<out T>(val conf: Config, val lambda: () -> T) {
operator fun getValue(receiver: Any, metadata: KProperty<*>): T {
return if (conf.hasPath(metadata.name)) conf.getValue(receiver, metadata) else lambda()
}
}
fun <T> Config.getOrElse(lambda: () -> T): OptionalConfig<T> {
return OptionalConfig(this, lambda)
}
fun Config.getProperties(path: String): Properties {
val obj = this.getObject(path)
val props = Properties()
for ((property, objectValue) in obj.entries) {
props.setProperty(property, objectValue.unwrapped().toString())
}
return props
}
class FullNodeConfiguration(conf: Config) : NodeConfiguration {
override val basedir: Path by conf
override val myLegalName: String by conf
override val nearestCity: String by conf
override val emailAddress: String by conf
class FullNodeConfiguration(config: Config) : NodeConfiguration {
override val basedir: Path by config
override val myLegalName: String by config
override val nearestCity: String by config
override val emailAddress: String by config
override val exportJMXto: String = "http"
override val keyStorePassword: String by conf
override val trustStorePassword: String by conf
override val dataSourceProperties: Properties by conf
override val devMode: Boolean by conf.getOrElse { false }
val networkMapAddress: HostAndPort? by conf.getOrElse { null }
val useHTTPS: Boolean by conf
val artemisAddress: HostAndPort by conf
val webAddress: HostAndPort by conf
val messagingServerAddress: HostAndPort? by conf.getOrElse { null }
val extraAdvertisedServiceIds: String by conf
override val keyStorePassword: String by config
override val trustStorePassword: String by config
override val dataSourceProperties: Properties by config
override val devMode: Boolean by config.getOrElse { false }
val networkMapAddress: HostAndPort? by config.getOrElse { null }
val useHTTPS: Boolean by config
val artemisAddress: HostAndPort by config
val webAddress: HostAndPort by config
val messagingServerAddress: HostAndPort? by config.getOrElse { null }
val extraAdvertisedServiceIds: String by config
fun createNode(): Node {
val advertisedServices = mutableSetOf<ServiceInfo>()

View File

@ -9,6 +9,7 @@ import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.core.serialization.SingletonSerializeAsToken
import com.r3corda.core.use
import com.r3corda.node.services.config.NodeSSLConfiguration
import com.r3corda.node.services.config.configureWithDevSSLCertificate
import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.TransportConfiguration
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory

View File

@ -9,6 +9,7 @@ import com.r3corda.core.crypto.X509Utilities.addOrReplaceKey
import com.r3corda.core.div
import com.r3corda.core.minutes
import com.r3corda.core.utilities.loggerFor
import com.r3corda.node.services.config.ConfigHelper
import com.r3corda.node.services.config.FullNodeConfiguration
import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.config.getValue
@ -130,7 +131,7 @@ fun main(args: Array<String>) {
val baseDirectoryPath = Paths.get(cmdlineOptions.valueOf(ParamsSpec.baseDirectoryArg))
val configFile = if (cmdlineOptions.has(ParamsSpec.configFileArg)) Paths.get(cmdlineOptions.valueOf(ParamsSpec.configFileArg)) else null
val config = NodeConfiguration.loadConfig(baseDirectoryPath, configFile, allowMissingConfig = true).let { config ->
val config = ConfigHelper.loadConfig(baseDirectoryPath, configFile, allowMissingConfig = true).let { config ->
object : NodeConfiguration by FullNodeConfiguration(config) {
val certificateSigningService: URL by config
}

View File

@ -21,6 +21,7 @@ import com.r3corda.demos.utilities.putJson
import com.r3corda.demos.utilities.uploadFile
import com.r3corda.node.internal.AbstractNode
import com.r3corda.node.internal.Node
import com.r3corda.node.services.config.ConfigHelper
import com.r3corda.node.services.config.FullNodeConfiguration
import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.messaging.NodeMessagingClient
@ -469,7 +470,7 @@ private fun loadConfigFile(baseDir: Path, configFile: Path, configOverrides: Map
createDefaultConfigFile(configFile, defaultLegalName)
log.warn("Default config created at $configFile.")
}
return FullNodeConfiguration(NodeConfiguration.loadConfig(baseDir, configFileOverride = configFile, configOverrides = configOverrides))
return FullNodeConfiguration(ConfigHelper.loadConfig(baseDir, configFileOverride = configFile, configOverrides = configOverrides))
}
private fun createIdentities(nodeConf: NodeConfiguration) {

View File

@ -10,8 +10,8 @@ import com.r3corda.core.utilities.Emoji
import com.r3corda.core.utilities.LogHelper
import com.r3corda.demos.api.NodeInterestRates
import com.r3corda.node.internal.Node
import com.r3corda.node.services.config.ConfigHelper
import com.r3corda.node.services.config.FullNodeConfiguration
import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.messaging.NodeMessagingClient
import com.r3corda.protocols.RatesFixProtocol
import joptsimple.OptionParser
@ -60,7 +60,7 @@ fun main(args: Array<String>) {
val apiAddr = HostAndPort.fromParts(myNetAddr.hostText, myNetAddr.port + 1)
val config = NodeConfiguration.loadConfig(
val config = ConfigHelper.loadConfig(
baseDirectoryPath = dir,
allowMissingConfig = true,
configOverrides = mapOf(

View File

@ -23,8 +23,8 @@ import com.r3corda.core.utilities.Emoji
import com.r3corda.core.utilities.LogHelper
import com.r3corda.core.utilities.ProgressTracker
import com.r3corda.node.internal.Node
import com.r3corda.node.services.config.ConfigHelper
import com.r3corda.node.services.config.FullNodeConfiguration
import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.messaging.NodeMessagingClient
import com.r3corda.node.services.network.NetworkMapService
import com.r3corda.node.services.persistence.NodeAttachmentService
@ -131,7 +131,7 @@ fun main(args: Array<String>) {
"webAddress" to apiNetAddr.toString(),
"h2port" to h2Port.toString()
)
FullNodeConfiguration(NodeConfiguration.loadConfig(directory, allowMissingConfig = true, configOverrides = configOverrides))
FullNodeConfiguration(ConfigHelper.loadConfig(directory, allowMissingConfig = true, configOverrides = configOverrides))
}
// Which services will this instance of the node provide to the network?

View File

@ -1,39 +1,27 @@
package com.r3corda.demos.attachment
import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.ListenableFuture
import com.r3corda.contracts.testing.fillWithSomeTestCash
import com.r3corda.core.contracts.Amount
import com.r3corda.core.contracts.DOLLARS
import com.r3corda.core.contracts.TransactionType
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.crypto.sha256
import com.r3corda.core.failure
import com.r3corda.core.logElapsedTime
import com.r3corda.core.node.services.ServiceInfo
import com.r3corda.core.node.services.ServiceType
import com.r3corda.core.success
import com.r3corda.core.transactions.SignedTransaction
import com.r3corda.core.utilities.Emoji
import com.r3corda.core.utilities.LogHelper
import com.r3corda.node.internal.Node
import com.r3corda.node.services.api.AbstractNodeService
import com.r3corda.node.services.config.ConfigHelper
import com.r3corda.node.services.config.FullNodeConfiguration
import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.messaging.NodeMessagingClient
import com.r3corda.node.services.network.NetworkMapService
import com.r3corda.node.services.persistence.NodeAttachmentService
import com.r3corda.node.services.transactions.SimpleNotaryService
import com.r3corda.node.utilities.databaseTransaction
import com.r3corda.protocols.FinalityProtocol
import com.r3corda.testing.ALICE_KEY
import joptsimple.OptionParser
import org.bouncycastle.cms.Recipient
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.nio.file.Paths
import java.util.*
import kotlin.concurrent.thread
import kotlin.system.exitProcess
import kotlin.test.assertEquals
@ -113,7 +101,7 @@ fun main(args: Array<String>) {
"artemisAddress" to myNetAddr.toString(),
"webAddress" to apiNetAddr.toString()
)
FullNodeConfiguration(NodeConfiguration.loadConfig(directory, allowMissingConfig = true, configOverrides = configOverrides))
FullNodeConfiguration(ConfigHelper.loadConfig(directory, allowMissingConfig = true, configOverrides = configOverrides))
}
// Which services will this instance of the node provide to the network?