CORDA-2491: Ability to specify Java package namespace from Cordform (#5075)

Add entry point with generic extra configuration options. Move configuration verification code to avoid circular dependencies.
This commit is contained in:
rui-r3
2019-05-09 17:25:21 +01:00
committed by Shams Asari
parent f0c75448b4
commit 9e3a0a64ac
5 changed files with 55 additions and 12 deletions

View File

@ -8,6 +8,7 @@ description 'Corda node API'
dependencies {
compile project(":core")
compile project(":serialization") // TODO Remove this once the NetworkBootstrapper class is moved into the tools:bootstrapper module
compile project(':common-configuration-parsing') // TODO Remove this dependency once NetworkBootsrapper is moved into tools:bootstrapper
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

View File

@ -3,6 +3,7 @@ package net.corda.nodeapi.internal.network
import com.typesafe.config.Config
import com.typesafe.config.ConfigException
import com.typesafe.config.ConfigFactory
import net.corda.common.configuration.parsing.internal.Configuration
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.*
@ -198,6 +199,23 @@ internal constructor(private val initSerEnv: Boolean,
bootstrap(directory, cordappJars, CopyCordapps.No, fromCordform = true)
}
/**
* Entry point for Cordform with extra configurations
* @param directory - directory on which the network will be deployed
* @param cordappJars - List of CordApps to deploy
* @param extraConfigurations - HOCON representation of extra configuration parameters
*/
fun bootstrapCordform(directory: Path, cordappJars: List<Path>, extraConfigurations: String) {
val configuration = ConfigFactory.parseString(extraConfigurations).resolve().getObject("networkParameterOverrides").toConfig().parseAsNetworkParametersConfiguration()
val networkParametersOverrides = configuration.doOnErrors(::reportErrors).optional ?: throw IllegalStateException("Invalid configuration passed.")
bootstrap(directory, cordappJars, CopyCordapps.No, fromCordform = true, networkParametersOverrides = networkParametersOverrides)
}
private fun reportErrors(errors: Set<Configuration.Validation.Error>) {
System.err.println("Error(s) found parsing the networkParameterOverrides:")
errors.forEach { System.err.println("Error parsing ${it.pathAsString}: ${it.message}") }
}
/** Entry point for the tool */
override fun bootstrap(directory: Path, copyCordapps: CopyCordapps, networkParameterOverrides: NetworkParametersOverrides) {
require(networkParameterOverrides.minimumPlatformVersion == null || networkParameterOverrides.minimumPlatformVersion <= PLATFORM_VERSION) { "Minimum platform version cannot be greater than $PLATFORM_VERSION" }

View File

@ -0,0 +1,104 @@
package net.corda.nodeapi.internal.network
import com.typesafe.config.Config
import net.corda.common.configuration.parsing.internal.Configuration
import net.corda.common.configuration.parsing.internal.get
import net.corda.common.configuration.parsing.internal.mapValid
import net.corda.common.configuration.parsing.internal.nested
import net.corda.common.validation.internal.Validated
import net.corda.core.internal.noPackageOverlap
import net.corda.core.internal.requirePackageValid
import net.corda.nodeapi.internal.crypto.loadKeyStore
import java.io.IOException
import java.nio.file.InvalidPathException
import java.nio.file.Path
import java.nio.file.Paths
import java.security.KeyStoreException
typealias Valid<TARGET> = Validated<TARGET, Configuration.Validation.Error>
fun Config.parseAsNetworkParametersConfiguration(options: Configuration.Validation.Options = Configuration.Validation.Options(strict = false)):
Valid<NetworkParametersOverrides> = NetworkParameterOverridesSpec.parse(this, options)
internal fun <T> badValue(msg: String): Valid<T> = Validated.invalid(sequenceOf(Configuration.Validation.Error.BadValue.of(msg)).toSet())
internal fun <T> valid(value: T): Valid<T> = Validated.valid(value)
internal object NetworkParameterOverridesSpec : Configuration.Specification<NetworkParametersOverrides>("DefaultNetworkParameters") {
private val minimumPlatformVersion by int().mapValid(NetworkParameterOverridesSpec::parsePositiveInteger).optional()
private val maxMessageSize by int().mapValid(NetworkParameterOverridesSpec::parsePositiveInteger).optional()
private val maxTransactionSize by int().mapValid(NetworkParameterOverridesSpec::parsePositiveInteger).optional()
private val packageOwnership by nested(PackageOwnershipSpec).list().optional()
private val eventHorizon by duration().optional()
internal object PackageOwnershipSpec : Configuration.Specification<PackageOwner>("PackageOwners") {
private val packageName by string().mapValid(PackageOwnershipSpec::toPackageName)
private val keystore by string().mapValid(PackageOwnershipSpec::toPath)
private val keystorePassword by string()
private val keystoreAlias by string()
override fun parseValid(configuration: Config): Validated<PackageOwner, Configuration.Validation.Error> {
val suppliedKeystorePath = configuration[keystore]
val keystorePassword = configuration[keystorePassword]
return try {
val javaPackageName = configuration[packageName]
val absoluteKeystorePath = if (suppliedKeystorePath.isAbsolute) {
suppliedKeystorePath
} else {
//If a relative path is supplied, make it relative to the location of the config file
Paths.get(configuration.origin().filename()).resolveSibling(suppliedKeystorePath.toString())
}.toAbsolutePath()
val ks = loadKeyStore(absoluteKeystorePath, keystorePassword)
return try {
val publicKey = ks.getCertificate(configuration[keystoreAlias]).publicKey
valid(PackageOwner(javaPackageName, publicKey))
} catch (kse: KeyStoreException) {
badValue("Keystore has not been initialized for alias ${configuration[keystoreAlias]}.")
}
} catch (kse: KeyStoreException) {
badValue("Password is incorrect or the key store is damaged for keyStoreFilePath: $suppliedKeystorePath.")
} catch (e: IOException) {
badValue("Error reading the key store from the file for keyStoreFilePath: $suppliedKeystorePath ${e.message}.")
}
}
private fun toPackageName(rawValue: String): Validated<String, Configuration.Validation.Error> {
return try {
requirePackageValid(rawValue)
valid(rawValue)
} catch (e: Exception) {
return badValue(e.message ?: e.toString())
}
}
private fun toPath(rawValue: String): Validated<Path, Configuration.Validation.Error> {
return try {
valid(Paths.get(rawValue))
} catch (e: InvalidPathException) {
return badValue("Path $rawValue not found")
}
}
}
override fun parseValid(configuration: Config): Valid<NetworkParametersOverrides> {
val packageOwnership = configuration[packageOwnership]
if (packageOwnership != null && !noPackageOverlap(packageOwnership.map { it.javaPackageName })) {
return Validated.invalid(sequenceOf(Configuration.Validation.Error.BadValue.of(
"Package namespaces must not overlap",
keyName = "packageOwnership",
containingPath = listOf()
)).toSet())
}
return valid(NetworkParametersOverrides(
minimumPlatformVersion = configuration[minimumPlatformVersion],
maxMessageSize = configuration[maxMessageSize],
maxTransactionSize = configuration[maxTransactionSize],
packageOwnership = packageOwnership,
eventHorizon = configuration[eventHorizon]
))
}
private fun parsePositiveInteger(rawValue: Int): Valid<Int> {
if (rawValue > 0) return valid(rawValue)
return badValue("The value must be at least 1")
}
}