Merge remote-tracking branch 'remotes/open/master' into merges/march-1-15-20

# Conflicts:
#	docs/source/corda-configuration-file.rst
#	docs/source/release-notes.rst
#	node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt
#	node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt
#	tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt
#	tools/explorer/README.md
#	tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt
This commit is contained in:
sollecitom
2018-03-01 15:36:40 +00:00
37 changed files with 386 additions and 181 deletions

View File

@ -29,6 +29,8 @@ import kotlin.reflect.jvm.jvmErasure
@Target(AnnotationTarget.PROPERTY)
annotation class OldConfig(val value: String)
const val CUSTOM_NODE_PROPERTIES_ROOT = "custom"
// TODO Move other config parsing to use parseAs and remove this
operator fun <T : Any> Config.getValue(receiver: Any, metadata: KProperty<*>): T {
return getValueInternal(metadata.name, metadata.returnType)
@ -37,9 +39,24 @@ operator fun <T : Any> Config.getValue(receiver: Any, metadata: KProperty<*>): T
fun <T : Any> Config.parseAs(clazz: KClass<T>): T {
require(clazz.isData) { "Only Kotlin data classes can be parsed. Offending: ${clazz.qualifiedName}" }
val constructor = clazz.primaryConstructor!!
val args = constructor.parameters
.filterNot { it.isOptional && !hasPath(it.name!!) }
.associateBy({ it }) { param ->
val parameters = constructor.parameters
val parameterNames = parameters.flatMap { param ->
mutableSetOf<String>().apply {
param.name?.let(this::add)
clazz.memberProperties.singleOrNull { it.name == param.name }?.let { matchingProperty ->
matchingProperty.annotations.filterIsInstance<OldConfig>().map { it.value }.forEach { this.add(it) }
}
}
}
val unknownConfigurationKeys = this.entrySet()
.mapNotNull { it.key.split(".").firstOrNull() }
.filterNot { it == CUSTOM_NODE_PROPERTIES_ROOT }
.filterNot(parameterNames::contains)
.toSortedSet()
if (unknownConfigurationKeys.isNotEmpty()) {
throw UnknownConfigurationKeysException.of(unknownConfigurationKeys)
}
val args = parameters.filterNot { it.isOptional && !hasPath(it.name!!) }.associateBy({ it }) { param ->
// Get the matching property for this parameter
val property = clazz.memberProperties.first { it.name == param.name }
val path = defaultToOldPath(property)
@ -48,6 +65,20 @@ fun <T : Any> Config.parseAs(clazz: KClass<T>): T {
return constructor.callBy(args)
}
class UnknownConfigurationKeysException private constructor(val unknownKeys: Set<String>) : IllegalArgumentException(message(unknownKeys)) {
init {
require(unknownKeys.isNotEmpty()) { "Absence of unknown keys should not raise UnknownConfigurationKeysException." }
}
companion object {
fun of(offendingKeys: Set<String>): UnknownConfigurationKeysException = UnknownConfigurationKeysException(offendingKeys)
private fun message(offendingKeys: Set<String>) = "Unknown configuration keys: ${offendingKeys.joinToString(", ", "[", "]")}."
}
}
inline fun <reified T : Any> Config.parseAs(): T = parseAs(T::class)
fun Config.toProperties(): Properties {

View File

@ -90,15 +90,17 @@ class NetworkBootstrapper {
}
private fun generateDirectoriesIfNeeded(directory: Path) {
val confFiles = directory.list { it.filter { it.toString().endsWith(".conf") }.toList() }
val confFiles = directory.list { it.filter { it.toString().endsWith("_node.conf") }.toList() }
val webServerConfFiles = directory.list { it.filter { it.toString().endsWith("_web-server.conf") }.toList() }
if (confFiles.isEmpty()) return
println("Node config files found in the root directory - generating node directories")
val cordaJar = extractCordaJarTo(directory)
for (confFile in confFiles) {
val nodeName = confFile.fileName.toString().removeSuffix(".conf")
val nodeName = confFile.fileName.toString().removeSuffix("_node.conf")
println("Generating directory for $nodeName")
val nodeDir = (directory / nodeName).createDirectories()
confFile.moveTo(nodeDir / "node.conf", StandardCopyOption.REPLACE_EXISTING)
webServerConfFiles.firstOrNull { directory.relativize(it).toString().removeSuffix("_web-server.conf") == nodeName }?.moveTo(nodeDir / "web-server.conf", StandardCopyOption.REPLACE_EXISTING)
Files.copy(cordaJar, (nodeDir / "corda.jar"), StandardCopyOption.REPLACE_EXISTING)
}
Files.delete(cordaJar)

View File

@ -200,6 +200,21 @@ class ConfigParsingTest {
assertThat(DataWithCompanion(3).toConfig()).isEqualTo(config("value" to 3))
}
@Test
fun `unknown configuration keys raise exception`() {
// intentional typo here, parsing should throw rather than sneakily return default value
val knownKey = "mandatory"
val unknownKey = "optioal"
val configuration = config(knownKey to "hello", unknownKey to "world")
assertThatThrownBy { configuration.parseAs<TypedConfiguration>() }.isInstanceOfSatisfying(UnknownConfigurationKeysException::class.java) { exception ->
assertThat(exception.unknownKeys).contains(unknownKey)
assertThat(exception.unknownKeys).doesNotContain(knownKey)
}
}
private inline fun <reified S : SingleData<V>, reified L : ListData<V>, V : Any> testPropertyType(
value1: V,
value2: V,
@ -243,6 +258,7 @@ class ConfigParsingTest {
val values: List<T>
}
data class TypedConfiguration(private val mandatory: String, private val optional: String = "optional")
data class StringData(override val value: String) : SingleData<String>
data class StringListData(override val values: List<String>) : ListData<String>
data class StringSetData(val values: Set<String>)