mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
ENT-11975: Updating config parsing with 4.12 version, which correctly parses the 4.12 rotated keys config item.
This commit is contained in:
parent
c9bbb5373a
commit
76b56679e6
@ -3,7 +3,13 @@
|
|||||||
|
|
||||||
package net.corda.nodeapi.internal.config
|
package net.corda.nodeapi.internal.config
|
||||||
|
|
||||||
import com.typesafe.config.*
|
import com.typesafe.config.Config
|
||||||
|
import com.typesafe.config.ConfigException
|
||||||
|
import com.typesafe.config.ConfigFactory
|
||||||
|
import com.typesafe.config.ConfigUtil
|
||||||
|
import com.typesafe.config.ConfigValue
|
||||||
|
import com.typesafe.config.ConfigValueFactory
|
||||||
|
import com.typesafe.config.ConfigValueType
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.isStatic
|
import net.corda.core.internal.isStatic
|
||||||
import net.corda.core.internal.noneOrSingle
|
import net.corda.core.internal.noneOrSingle
|
||||||
@ -11,9 +17,7 @@ import net.corda.core.internal.uncheckedCast
|
|||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.lang.reflect.Field
|
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.lang.reflect.ParameterizedType
|
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -22,7 +26,9 @@ import java.time.Duration
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.temporal.Temporal
|
import java.time.temporal.Temporal
|
||||||
import java.util.*
|
import java.time.temporal.TemporalAmount
|
||||||
|
import java.util.Properties
|
||||||
|
import java.util.UUID
|
||||||
import javax.security.auth.x500.X500Principal
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
@ -181,7 +187,7 @@ private fun Config.getSingleValue(
|
|||||||
X500Principal::class -> X500Principal(getString(path))
|
X500Principal::class -> X500Principal(getString(path))
|
||||||
CordaX500Name::class -> {
|
CordaX500Name::class -> {
|
||||||
when (getValue(path).valueType()) {
|
when (getValue(path).valueType()) {
|
||||||
ConfigValueType.OBJECT -> getConfig(path).parseAs(onUnknownKeys)
|
ConfigValueType.OBJECT -> getConfig(path).parseAs(onUnknownKeys) as CordaX500Name
|
||||||
else -> CordaX500Name.parse(getString(path))
|
else -> CordaX500Name.parse(getString(path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -291,98 +297,39 @@ private fun <T : Enum<T>> enumBridge(clazz: Class<T>, name: String): T {
|
|||||||
*/
|
*/
|
||||||
fun Any.toConfig(): Config = ConfigValueFactory.fromMap(toConfigMap()).toConfig()
|
fun Any.toConfig(): Config = ConfigValueFactory.fromMap(toConfigMap()).toConfig()
|
||||||
|
|
||||||
fun Any?.toConfigValue(): ConfigValue = if (this is ConfigValue) {
|
fun Any?.toConfigValue(): ConfigValue = ConfigValueFactory.fromAnyRef(sanitiseForFromAnyRef(this))
|
||||||
this
|
|
||||||
} else if (this != null) {
|
|
||||||
ConfigValueFactory.fromAnyRef(convertValue(this))
|
|
||||||
} else {
|
|
||||||
ConfigValueFactory.fromAnyRef(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
|
||||||
// Reflect over the fields of the receiver and generate a value Map that can use to create Config object.
|
// Reflect over the fields of the receiver and generate a value Map that can use to create Config object.
|
||||||
private fun Any.toConfigMap(): Map<String, Any> {
|
private fun Any.toConfigMap(): Map<String, Any?> {
|
||||||
val values = HashMap<String, Any>()
|
val values = LinkedHashMap<String, Any?>()
|
||||||
for (field in javaClass.declaredFields) {
|
for (field in javaClass.declaredFields) {
|
||||||
if (field.isStatic || field.isSynthetic) continue
|
if (field.isStatic || field.isSynthetic) continue
|
||||||
field.isAccessible = true
|
field.isAccessible = true
|
||||||
val value = field.get(this) ?: continue
|
val value = field.get(this) ?: continue
|
||||||
val configValue = if (value is String || value is Boolean || value is Number) {
|
values[field.name] = sanitiseForFromAnyRef(value)
|
||||||
// These types are supported by Config as use as is
|
|
||||||
value
|
|
||||||
} else if (value is Temporal || value is NetworkHostAndPort || value is CordaX500Name ||
|
|
||||||
value is Path || value is URL || value is UUID || value is X500Principal) {
|
|
||||||
// These types make sense to be represented as Strings and the exact inverse parsing function for use in parseAs
|
|
||||||
value.toString()
|
|
||||||
} else if (value is Enum<*>) {
|
|
||||||
// Expicitly use the Enum's name in case the toString is overridden, which would make parsing problematic.
|
|
||||||
value.name
|
|
||||||
} else if (value is Properties) {
|
|
||||||
// For Properties we treat keys with . as nested configs
|
|
||||||
ConfigFactory.parseMap(uncheckedCast(value)).root()
|
|
||||||
} else if (value is Iterable<*>) {
|
|
||||||
value.toConfigIterable(field)
|
|
||||||
} else {
|
|
||||||
// Else this is a custom object recursed over
|
|
||||||
value.toConfigMap()
|
|
||||||
}
|
|
||||||
values[field.name] = configValue
|
|
||||||
}
|
}
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertValue(value: Any): Any {
|
/**
|
||||||
|
* @see ConfigValueFactory.fromAnyRef
|
||||||
return if (value is String || value is Boolean || value is Number) {
|
*/
|
||||||
|
private fun sanitiseForFromAnyRef(value: Any?): Any? {
|
||||||
|
return when (value) {
|
||||||
// These types are supported by Config as use as is
|
// These types are supported by Config as use as is
|
||||||
value
|
is String, is Boolean, is Number, is ConfigValue, is Duration, null -> value
|
||||||
} else if (value is Temporal || value is NetworkHostAndPort || value is CordaX500Name ||
|
is Enum<*> -> value.name
|
||||||
value is Path || value is URL || value is UUID || value is X500Principal) {
|
// These types make sense to be represented as Strings
|
||||||
// These types make sense to be represented as Strings and the exact inverse parsing function for use in parseAs
|
is Temporal, is TemporalAmount, is NetworkHostAndPort, is CordaX500Name, is Path, is URL, is UUID, is X500Principal -> value.toString()
|
||||||
value.toString()
|
|
||||||
} else if (value is Enum<*>) {
|
|
||||||
// Expicitly use the Enum's name in case the toString is overridden, which would make parsing problematic.
|
|
||||||
value.name
|
|
||||||
} else if (value is Properties) {
|
|
||||||
// For Properties we treat keys with . as nested configs
|
// For Properties we treat keys with . as nested configs
|
||||||
ConfigFactory.parseMap(uncheckedCast(value)).root()
|
is Properties -> ConfigFactory.parseMap(uncheckedCast(value)).root()
|
||||||
} else if (value is Iterable<*>) {
|
is Map<*, *> -> ConfigFactory.parseMap(value.map { it.key.toString() to sanitiseForFromAnyRef(it.value) }.toMap()).root()
|
||||||
value.toConfigIterable()
|
is Iterable<*> -> value.map(::sanitiseForFromAnyRef)
|
||||||
} else {
|
|
||||||
// Else this is a custom object recursed over
|
// Else this is a custom object recursed over
|
||||||
value.toConfigMap()
|
else -> value.toConfigMap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For Iterables figure out the type parameter and apply the same logic as above on the individual elements.
|
|
||||||
private fun Iterable<*>.toConfigIterable(field: Field): Iterable<Any?> {
|
|
||||||
val elementType = (field.genericType as ParameterizedType).actualTypeArguments[0] as Class<*>
|
|
||||||
return when (elementType) {
|
|
||||||
// For the types already supported by Config we can use the Iterable as is
|
|
||||||
String::class.java -> this
|
|
||||||
Integer::class.java -> this
|
|
||||||
java.lang.Long::class.java -> this
|
|
||||||
java.lang.Double::class.java -> this
|
|
||||||
java.lang.Boolean::class.java -> this
|
|
||||||
LocalDate::class.java -> map(Any?::toString)
|
|
||||||
Instant::class.java -> map(Any?::toString)
|
|
||||||
NetworkHostAndPort::class.java -> map(Any?::toString)
|
|
||||||
Path::class.java -> map(Any?::toString)
|
|
||||||
URL::class.java -> map(Any?::toString)
|
|
||||||
X500Principal::class.java -> map(Any?::toString)
|
|
||||||
UUID::class.java -> map(Any?::toString)
|
|
||||||
CordaX500Name::class.java -> map(Any?::toString)
|
|
||||||
Properties::class.java -> map { ConfigFactory.parseMap(uncheckedCast(it)).root() }
|
|
||||||
else -> if (elementType.isEnum) {
|
|
||||||
map { (it as Enum<*>).name }
|
|
||||||
} else {
|
|
||||||
map { it?.toConfigMap() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Iterable<*>.toConfigIterable(): Iterable<Any?> = map { element -> element?.let(::convertValue) }
|
|
||||||
|
|
||||||
// The typesafe .getBoolean function is case sensitive, this is a case insensitive version
|
// The typesafe .getBoolean function is case sensitive, this is a case insensitive version
|
||||||
fun Config.getBooleanCaseInsensitive(path: String): Boolean {
|
fun Config.getBooleanCaseInsensitive(path: String): Boolean {
|
||||||
try {
|
try {
|
||||||
|
Loading…
Reference in New Issue
Block a user