[CORDA-1993]: Replace reflection-based NodeConfiguration parsing with versioned property-based parsing mechanism. (#4132)

This commit is contained in:
Michele Sollecito
2018-11-08 15:56:00 +00:00
committed by GitHub
parent 9277042db8
commit 6c749889d0
34 changed files with 1161 additions and 513 deletions

View File

@ -224,6 +224,8 @@ private fun <T : Enum<T>> enumBridge(clazz: Class<T>, name: String): T {
*/
fun Any.toConfig(): Config = ConfigValueFactory.fromMap(toConfigMap()).toConfig()
fun Any.toConfigValue(): ConfigValue = if (this is ConfigValue) this else ConfigValueFactory.fromAnyRef(convertValue(this))
@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.
private fun Any.toConfigMap(): Map<String, Any> {
@ -255,6 +257,28 @@ private fun Any.toConfigMap(): Map<String, Any> {
return values
}
private fun convertValue(value: Any): Any {
return if (value is String || value is Boolean || value is Number) {
// 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()
} else {
// Else this is a custom object recursed over
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<*>
@ -282,6 +306,8 @@ private fun Iterable<*>.toConfigIterable(field: Field): Iterable<Any?> {
}
}
private fun Iterable<*>.toConfigIterable(): Iterable<Any?> = map { element -> element?.let(::convertValue) }
// The typesafe .getBoolean function is case sensitive, this is a case insensitive version
fun Config.getBooleanCaseInsensitive(path: String): Boolean {
try {
@ -300,7 +326,6 @@ private val logger = LoggerFactory.getLogger("net.corda.nodeapi.internal.config"
enum class UnknownConfigKeysPolicy(private val handle: (Set<String>, logger: Logger) -> Unit) {
FAIL({ unknownKeys, _ -> throw UnknownConfigurationKeysException.of(unknownKeys) }),
WARN({ unknownKeys, logger -> logger.warn("Unknown configuration keys found: ${unknownKeys.joinToString(", ", "[", "]")}.") }),
IGNORE({ _, _ -> });
fun handle(unknownKeys: Set<String>, logger: Logger) {

View File

@ -22,11 +22,18 @@ const val NODE_DATABASE_PREFIX = "node_"
// This class forms part of the node config and so any changes to it must be handled with care
data class DatabaseConfig(
val initialiseSchema: Boolean = true,
val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ,
val exportHibernateJMXStatistics: Boolean = false,
val mappedSchemaCacheSize: Long = 100
)
val initialiseSchema: Boolean = Defaults.initialiseSchema,
val transactionIsolationLevel: TransactionIsolationLevel = Defaults.transactionIsolationLevel,
val exportHibernateJMXStatistics: Boolean = Defaults.exportHibernateJMXStatistics,
val mappedSchemaCacheSize: Long = Defaults.mappedSchemaCacheSize
) {
object Defaults {
val initialiseSchema = true
val transactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ
val exportHibernateJMXStatistics = false
val mappedSchemaCacheSize = 100L
}
}
// This class forms part of the node config and so any changes to it must be handled with care
enum class TransactionIsolationLevel {