mirror of
https://github.com/corda/corda.git
synced 2024-12-26 08:01:09 +00:00
ENT-4494 Harmonize configuration classes.
This commit is contained in:
parent
68bb7a0e7b
commit
53d92771bb
@ -60,6 +60,7 @@ interface CertificateStore : Iterable<Pair<String, X509Certificate>> {
|
|||||||
forEach { (alias, certificate) -> action.invoke(alias, certificate) }
|
forEach { (alias, certificate) -> action.invoke(alias, certificate) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun aliases(): List<String> = value.internal.aliases().toList()
|
||||||
/**
|
/**
|
||||||
* @throws IllegalArgumentException if no certificate for the alias is found, or if the certificate is not an [X509Certificate].
|
* @throws IllegalArgumentException if no certificate for the alias is found, or if the certificate is not an [X509Certificate].
|
||||||
*/
|
*/
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@file:JvmName("ConfigUtilities")
|
@file:JvmName("ConfigUtilities")
|
||||||
|
@file:Suppress("LongParameterList")
|
||||||
|
|
||||||
package net.corda.nodeapi.internal.config
|
package net.corda.nodeapi.internal.config
|
||||||
|
|
||||||
@ -52,21 +53,35 @@ const val CUSTOM_NODE_PROPERTIES_ROOT = "custom"
|
|||||||
// This is to enable constructs like:
|
// This is to enable constructs like:
|
||||||
// `val keyStorePassword: String by config`
|
// `val keyStorePassword: String by config`
|
||||||
operator fun <T : Any> Config.getValue(receiver: Any, metadata: KProperty<*>): T {
|
operator fun <T : Any> Config.getValue(receiver: Any, metadata: KProperty<*>): T {
|
||||||
return getValueInternal(metadata.name, metadata.returnType, UnknownConfigKeysPolicy.IGNORE::handle, nestedPath = null, baseDirectory = null)
|
return getValueInternal(
|
||||||
|
metadata.name,
|
||||||
|
metadata.returnType,
|
||||||
|
UnknownConfigKeysPolicy.IGNORE::handle,
|
||||||
|
nestedPath = null,
|
||||||
|
baseDirectory = null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Problems:
|
// Problems:
|
||||||
// - Forces you to have a primary constructor with all fields of name and type matching the configuration file structure.
|
// - Forces you to have a primary constructor with all fields of name and type matching the configuration file structure.
|
||||||
// - Encourages weak bean-like types.
|
// - Encourages weak bean-like types.
|
||||||
// - Cannot support a many-to-one relationship between configuration file structures and configuration domain type. This is essential for versioning of the configuration files.
|
// - Cannot support a many-to-one relationship between configuration file structures and configuration domain type. This is essential for
|
||||||
|
// versioning of the configuration files.
|
||||||
// - It's complicated and based on reflection, meaning problems with it are typically found at runtime.
|
// - It's complicated and based on reflection, meaning problems with it are typically found at runtime.
|
||||||
// - It doesn't support validation errors in a structured way. If something goes wrong, it throws exceptions, which doesn't support good usability practices like displaying all the errors at once.
|
// - It doesn't support validation errors in a structured way. If something goes wrong, it throws exceptions, which doesn't support good
|
||||||
fun <T : Any> Config.parseAs(clazz: KClass<T>, onUnknownKeys: ((Set<String>, logger: Logger) -> Unit) = UnknownConfigKeysPolicy.FAIL::handle,
|
// usability practices like displaying all the errors at once.
|
||||||
nestedPath: String? = null, baseDirectory: Path? = null): T {
|
fun <T : Any> Config.parseAs(
|
||||||
|
clazz: KClass<T>,
|
||||||
|
onUnknownKeys: ((Set<String>, logger: Logger) -> Unit) = UnknownConfigKeysPolicy.FAIL::handle,
|
||||||
|
nestedPath: String? = null,
|
||||||
|
baseDirectory: Path? = null
|
||||||
|
): T {
|
||||||
// Use custom parser if provided, instead of treating the object as data class.
|
// Use custom parser if provided, instead of treating the object as data class.
|
||||||
clazz.findAnnotation<CustomConfigParser>()?.let { return uncheckedCast(it.parser.createInstance().parse(this)) }
|
clazz.findAnnotation<CustomConfigParser>()?.let { return uncheckedCast(it.parser.createInstance().parse(this)) }
|
||||||
|
|
||||||
require(clazz.isData) { "Only Kotlin data classes or class annotated with CustomConfigParser can be parsed. Offending: ${clazz.qualifiedName}" }
|
require(clazz.isData) {
|
||||||
|
"Only Kotlin data classes or class annotated with CustomConfigParser can be parsed. Offending: ${clazz.qualifiedName}"
|
||||||
|
}
|
||||||
val constructor = clazz.primaryConstructor!!
|
val constructor = clazz.primaryConstructor!!
|
||||||
val parameters = constructor.parameters
|
val parameters = constructor.parameters
|
||||||
val parameterNames = parameters.flatMap { param ->
|
val parameterNames = parameters.flatMap { param ->
|
||||||
@ -104,24 +119,46 @@ class UnknownConfigurationKeysException private constructor(val unknownKeys: Set
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun of(offendingKeys: Set<String>): UnknownConfigurationKeysException = UnknownConfigurationKeysException(offendingKeys)
|
fun of(offendingKeys: Set<String>): UnknownConfigurationKeysException = UnknownConfigurationKeysException(offendingKeys)
|
||||||
private fun message(offendingKeys: Set<String>) = "Unknown configuration keys: ${offendingKeys.joinToString(", ", "[", "]")}."
|
private fun message(offendingKeys: Set<String>) = "Unknown configuration keys: " +
|
||||||
|
"${offendingKeys.joinToString(", ", "[", "]")}."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T : Any> Config.parseAs(noinline onUnknownKeys: ((Set<String>, logger: Logger) -> Unit) = UnknownConfigKeysPolicy.FAIL::handle): T = parseAs(T::class, onUnknownKeys)
|
inline fun <reified T : Any> Config.parseAs(
|
||||||
|
noinline onUnknownKeys: ((Set<String>, logger: Logger) -> Unit) = UnknownConfigKeysPolicy.FAIL::handle
|
||||||
|
): T = parseAs(T::class, onUnknownKeys)
|
||||||
|
|
||||||
fun Config.toProperties(): Properties {
|
fun Config.toProperties(): Properties {
|
||||||
return entrySet().associateByTo(
|
return entrySet().associateByTo(
|
||||||
Properties(),
|
Properties(),
|
||||||
{ ConfigUtil.splitPath(it.key).joinToString(".") },
|
{ ConfigUtil.splitPath(it.key).joinToString(".") },
|
||||||
{ it.value.unwrapped().toString() })
|
{ it.value.unwrapped() })
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T : Any> Config.getValueInternal(path: String, type: KType, onUnknownKeys: ((Set<String>, logger: Logger) -> Unit), nestedPath: String?, baseDirectory: Path?): T {
|
private fun <T : Any> Config.getValueInternal(
|
||||||
return uncheckedCast(if (type.arguments.isEmpty()) getSingleValue(path, type, onUnknownKeys, nestedPath, baseDirectory) else getCollectionValue(path, type, onUnknownKeys, nestedPath, baseDirectory))
|
path: String,
|
||||||
|
type: KType,
|
||||||
|
onUnknownKeys: ((Set<String>, logger: Logger) -> Unit),
|
||||||
|
nestedPath: String?,
|
||||||
|
baseDirectory: Path?
|
||||||
|
): T {
|
||||||
|
return uncheckedCast(
|
||||||
|
if (type.arguments.isEmpty()) {
|
||||||
|
getSingleValue(path, type, onUnknownKeys, nestedPath, baseDirectory)
|
||||||
|
} else {
|
||||||
|
getCollectionValue(path, type, onUnknownKeys, nestedPath, baseDirectory)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Config.getSingleValue(path: String, type: KType, onUnknownKeys: (Set<String>, logger: Logger) -> Unit, nestedPath: String?, baseDirectory: Path?): Any? {
|
@Suppress("ComplexMethod")
|
||||||
|
private fun Config.getSingleValue(
|
||||||
|
path: String,
|
||||||
|
type: KType,
|
||||||
|
onUnknownKeys: (Set<String>, logger: Logger) -> Unit,
|
||||||
|
nestedPath: String?,
|
||||||
|
baseDirectory: Path?
|
||||||
|
): Any? {
|
||||||
if (type.isMarkedNullable && !hasPath(path)) return null
|
if (type.isMarkedNullable && !hasPath(path)) return null
|
||||||
val typeClass = type.jvmErasure
|
val typeClass = type.jvmErasure
|
||||||
return try {
|
return try {
|
||||||
@ -153,7 +190,12 @@ private fun Config.getSingleValue(path: String, type: KType, onUnknownKeys: (Set
|
|||||||
else -> if (typeClass.java.isEnum) {
|
else -> if (typeClass.java.isEnum) {
|
||||||
parseEnum(typeClass.java, getString(path))
|
parseEnum(typeClass.java, getString(path))
|
||||||
} else {
|
} else {
|
||||||
getConfig(path).parseAs(typeClass, onUnknownKeys, nestedPath?.let { "$it.$path" } ?: path, baseDirectory = baseDirectory)
|
getConfig(path).parseAs(
|
||||||
|
typeClass,
|
||||||
|
onUnknownKeys,
|
||||||
|
nestedPath?.let { "$it.$path" } ?: path,
|
||||||
|
baseDirectory = baseDirectory
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: ConfigException.Missing) {
|
} catch (e: ConfigException.Missing) {
|
||||||
@ -164,7 +206,8 @@ private fun Config.getSingleValue(path: String, type: KType, onUnknownKeys: (Set
|
|||||||
private fun resolvePath(pathAsString: String, baseDirectory: Path?): Path {
|
private fun resolvePath(pathAsString: String, baseDirectory: Path?): Path {
|
||||||
val path = Paths.get(pathAsString)
|
val path = Paths.get(pathAsString)
|
||||||
return if (baseDirectory != null) {
|
return if (baseDirectory != null) {
|
||||||
// if baseDirectory been specified try resolving path against it. Note if `pathFromConfig` is an absolute path - this instruction has no effect.
|
// if baseDirectory been specified try resolving path against it. Note if `pathFromConfig` is an absolute path - this instruction
|
||||||
|
// has no effect.
|
||||||
baseDirectory.resolve(path)
|
baseDirectory.resolve(path)
|
||||||
} else {
|
} else {
|
||||||
path
|
path
|
||||||
@ -178,10 +221,18 @@ private fun ConfigException.Missing.relative(path: String, nestedPath: String?):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Config.getCollectionValue(path: String, type: KType, onUnknownKeys: (Set<String>, logger: Logger) -> Unit, nestedPath: String?, baseDirectory: Path?): Collection<Any> {
|
@Suppress("ComplexMethod")
|
||||||
|
private fun Config.getCollectionValue(
|
||||||
|
path: String,
|
||||||
|
type: KType,
|
||||||
|
onUnknownKeys: (Set<String>, logger: Logger) -> Unit,
|
||||||
|
nestedPath: String?,
|
||||||
|
baseDirectory: Path?
|
||||||
|
): Collection<Any> {
|
||||||
val typeClass = type.jvmErasure
|
val typeClass = type.jvmErasure
|
||||||
require(typeClass == List::class || typeClass == Set::class) { "$typeClass is not supported" }
|
require(typeClass == List::class || typeClass == Set::class) { "$typeClass is not supported" }
|
||||||
val elementClass = type.arguments[0].type?.jvmErasure ?: throw IllegalArgumentException("Cannot work with star projection: $type")
|
val elementClass = type.arguments[0].type?.jvmErasure
|
||||||
|
?: throw IllegalArgumentException("Cannot work with star projection: $type")
|
||||||
if (!hasPath(path)) {
|
if (!hasPath(path)) {
|
||||||
return if (typeClass == List::class) emptyList() else emptySet()
|
return if (typeClass == List::class) emptyList() else emptySet()
|
||||||
}
|
}
|
||||||
@ -240,7 +291,13 @@ 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) this else if (this != null) ConfigValueFactory.fromAnyRef(convertValue(this)) else ConfigValueFactory.fromAnyRef(null)
|
fun Any?.toConfigValue(): ConfigValue = if (this is ConfigValue) {
|
||||||
|
this
|
||||||
|
} else if (this != null) {
|
||||||
|
ConfigValueFactory.fromAnyRef(convertValue(this))
|
||||||
|
} else {
|
||||||
|
ConfigValueFactory.fromAnyRef(null)
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
@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.
|
||||||
@ -253,7 +310,8 @@ private fun Any.toConfigMap(): Map<String, Any> {
|
|||||||
val configValue = if (value is String || value is Boolean || value is Number) {
|
val configValue = if (value is String || value is Boolean || value is Number) {
|
||||||
// These types are supported by Config as use as is
|
// These types are supported by Config as use as is
|
||||||
value
|
value
|
||||||
} else if (value is Temporal || value is NetworkHostAndPort || value is CordaX500Name || value is Path || value is URL || value is UUID || value is X500Principal) {
|
} 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
|
// These types make sense to be represented as Strings and the exact inverse parsing function for use in parseAs
|
||||||
value.toString()
|
value.toString()
|
||||||
} else if (value is Enum<*>) {
|
} else if (value is Enum<*>) {
|
||||||
@ -278,7 +336,8 @@ private fun convertValue(value: Any): Any {
|
|||||||
return if (value is String || value is Boolean || value is Number) {
|
return if (value is String || value is Boolean || value is Number) {
|
||||||
// These types are supported by Config as use as is
|
// These types are supported by Config as use as is
|
||||||
value
|
value
|
||||||
} else if (value is Temporal || value is NetworkHostAndPort || value is CordaX500Name || value is Path || value is URL || value is UUID || value is X500Principal) {
|
} 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
|
// These types make sense to be represented as Strings and the exact inverse parsing function for use in parseAs
|
||||||
value.toString()
|
value.toString()
|
||||||
} else if (value is Enum<*>) {
|
} else if (value is Enum<*>) {
|
||||||
|
@ -4,6 +4,7 @@ interface SslConfiguration {
|
|||||||
|
|
||||||
val keyStore: FileBasedCertificateStoreSupplier?
|
val keyStore: FileBasedCertificateStoreSupplier?
|
||||||
val trustStore: FileBasedCertificateStoreSupplier?
|
val trustStore: FileBasedCertificateStoreSupplier?
|
||||||
|
val useOpenSsl: Boolean
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@ -19,4 +20,10 @@ interface MutualSslConfiguration : SslConfiguration {
|
|||||||
override val trustStore: FileBasedCertificateStoreSupplier
|
override val trustStore: FileBasedCertificateStoreSupplier
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MutualSslOptions(override val keyStore: FileBasedCertificateStoreSupplier, override val trustStore: FileBasedCertificateStoreSupplier) : MutualSslConfiguration
|
private class MutualSslOptions(override val keyStore: FileBasedCertificateStoreSupplier,
|
||||||
|
override val trustStore: FileBasedCertificateStoreSupplier) : MutualSslConfiguration {
|
||||||
|
override val useOpenSsl: Boolean = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const val DEFAULT_SSL_HANDSHAKE_TIMEOUT_MILLIS = 60000L // Set at least 3 times higher than sun.security.provider.certpath.URICertStore.DEFAULT_CRL_CONNECT_TIMEOUT which is 15 sec
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user