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
}