mirror of
https://github.com/corda/corda.git
synced 2025-01-31 16:35:43 +00:00
Merged in mnesbit-cor-261-deployable-nodes (pull request #269)
Mnesbit cor 261 deployable nodes
This commit is contained in:
commit
7b63381282
109
build.gradle
109
build.gradle
@ -1,40 +1,3 @@
|
||||
group 'com.r3corda'
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'application'
|
||||
apply plugin: 'project-report'
|
||||
apply plugin: QuasarPlugin
|
||||
apply plugin: 'com.github.ben-manes.versions'
|
||||
|
||||
allprojects {
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'jacoco'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
|
||||
}
|
||||
|
||||
// Our version: bump this on release.
|
||||
group 'com.r3corda'
|
||||
version '0.3-SNAPSHOT'
|
||||
}
|
||||
|
||||
subprojects {
|
||||
jacocoTestReport {
|
||||
additionalSourceDirs = files(sourceSets.main.allSource.srcDirs)
|
||||
sourceDirectories = files(sourceSets.main.allSource.srcDirs)
|
||||
classDirectories = files(sourceSets.main.output)
|
||||
reports {
|
||||
html.enabled = true
|
||||
xml.enabled = true
|
||||
csv.enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.0.3'
|
||||
ext.quasar_version = '0.7.5'
|
||||
@ -60,6 +23,34 @@ buildscript {
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
// TODO The capsule plugin requires the newer DSL plugin block.It would be nice if we could unify all the plugins into one style,
|
||||
// but the DSL has some restrictions e.g can't be used on the allprojects section. So we should revisit this if there are improvements in Gradle.
|
||||
id "us.kirchmeier.capsule" version "1.0.2"
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'application'
|
||||
apply plugin: 'project-report'
|
||||
apply plugin: QuasarPlugin
|
||||
apply plugin: 'com.github.ben-manes.versions'
|
||||
|
||||
allprojects {
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'jacoco'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
|
||||
}
|
||||
|
||||
// Our version: bump this on release.
|
||||
group 'com.r3corda'
|
||||
version '0.3-SNAPSHOT'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
@ -200,4 +191,48 @@ applicationDistribution.into("bin") {
|
||||
from(getIRSDemo)
|
||||
from(getTraderDemo)
|
||||
fileMode = 0755
|
||||
}
|
||||
|
||||
task createCapsule(type: FatCapsule, dependsOn: 'quasarScan') {
|
||||
applicationClass 'com.r3corda.node.MainKt'
|
||||
|
||||
capsuleManifest {
|
||||
appClassPath = ["jolokia-agent-war-${project.ext.jolokia_version}.war"]
|
||||
systemProperties['log4j.configuration'] = 'log4j2.xml'
|
||||
javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"]
|
||||
minJavaVersion = '1.8.0'
|
||||
}
|
||||
}
|
||||
|
||||
task createStandalone(dependsOn: 'createCapsule') << {
|
||||
copy {
|
||||
from createCapsule.outputs.getFiles()
|
||||
from 'config/dev/nameservernode.conf'
|
||||
into "${buildDir}/standalone/nameserver"
|
||||
rename 'nameservernode.conf', 'node.conf'
|
||||
}
|
||||
|
||||
copy {
|
||||
from createCapsule.outputs.getFiles()
|
||||
from 'config/dev/generalnodea.conf'
|
||||
into "${buildDir}/standalone/nodea"
|
||||
rename 'generalnodea.conf', 'node.conf'
|
||||
}
|
||||
|
||||
copy {
|
||||
from createCapsule.outputs.getFiles()
|
||||
from 'config/dev/generalnodeb.conf'
|
||||
into "${buildDir}/standalone/nodeb"
|
||||
rename 'generalnodeb.conf', 'node.conf'
|
||||
}
|
||||
|
||||
delete("${buildDir}/standalone/runstandalone")
|
||||
def jarName = createCapsule.outputs.getFiles().getSingleFile().getName()
|
||||
copy {
|
||||
from "buildSrc/scripts/runstandalone"
|
||||
filter { String line -> line.replace("JAR_NAME", jarName) }
|
||||
filter(org.apache.tools.ant.filters.FixCrLfFilter.class, eol: org.apache.tools.ant.filters.FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
into "${buildDir}/standalone"
|
||||
}
|
||||
|
||||
}
|
15
buildSrc/scripts/runstandalone
Normal file
15
buildSrc/scripts/runstandalone
Normal file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
trap 'kill $(jobs -p)' SIGINT SIGTERM EXIT
|
||||
export CAPSULE_CACHE_DIR=cache
|
||||
pushd nameserver
|
||||
( java -jar JAR_NAME )&
|
||||
popd
|
||||
pushd nodea
|
||||
( java -jar JAR_NAME )&
|
||||
popd
|
||||
pushd nodeb
|
||||
( java -jar JAR_NAME )&
|
||||
popd
|
||||
read -p 'Any key to exit'
|
||||
kill $(jobs -p)
|
14
config/dev/generalnodea.conf
Normal file
14
config/dev/generalnodea.conf
Normal file
@ -0,0 +1,14 @@
|
||||
basedir : "./nodea",
|
||||
myLegalName : "Bank A",
|
||||
nearestCity : "London",
|
||||
keyStorePassword : "cordacadevpass",
|
||||
trustStorePassword : "trustpass",
|
||||
artemisAddress : "localhost:31337",
|
||||
webAddress : "localhost:31339",
|
||||
hostNotaryServiceLocally: false,
|
||||
extraAdvertisedServiceIds: "corda.interest_rates",
|
||||
mapService : {
|
||||
hostServiceLocally : false,
|
||||
address : "localhost:12345",
|
||||
identity : "Notary Service"
|
||||
}
|
14
config/dev/generalnodeb.conf
Normal file
14
config/dev/generalnodeb.conf
Normal file
@ -0,0 +1,14 @@
|
||||
basedir : "./nodeb",
|
||||
myLegalName : "Bank B",
|
||||
nearestCity : "London",
|
||||
keyStorePassword : "cordacadevpass",
|
||||
trustStorePassword : "trustpass",
|
||||
artemisAddress : "localhost:31338",
|
||||
webAddress : "localhost:31340",
|
||||
hostNotaryServiceLocally: false,
|
||||
extraAdvertisedServiceIds: "corda.interest_rates",
|
||||
mapService : {
|
||||
hostServiceLocally : false,
|
||||
address : "localhost:12345",
|
||||
identity : "Notary Service"
|
||||
}
|
14
config/dev/nameservernode.conf
Normal file
14
config/dev/nameservernode.conf
Normal file
@ -0,0 +1,14 @@
|
||||
basedir : "./nameserver",
|
||||
myLegalName : "Notary Service",
|
||||
nearestCity : "London",
|
||||
keyStorePassword : "cordacadevpass",
|
||||
trustStorePassword : "trustpass",
|
||||
artemisAddress : "localhost:12345",
|
||||
webAddress : "localhost:12346",
|
||||
hostNotaryServiceLocally: true,
|
||||
extraAdvertisedServiceIds: "",
|
||||
mapService : {
|
||||
hostServiceLocally : true,
|
||||
address : ${artemisAddress},
|
||||
identity : ${myLegalName}
|
||||
}
|
@ -21,8 +21,15 @@ import javax.net.ssl.*
|
||||
*/
|
||||
fun registerWhitelistTrustManager() {
|
||||
if (Security.getProvider("WhitelistTrustManager") == null) {
|
||||
Security.addProvider(WhitelistTrustManagerProvider)
|
||||
WhitelistTrustManagerProvider.register()
|
||||
}
|
||||
|
||||
// Forcibly change the TrustManagerFactory defaultAlgorithm to be us
|
||||
// This will apply to all code using TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||
// Which includes the standard HTTPS implementation and most other SSL code
|
||||
// TrustManagerFactory.getInstance(WhitelistTrustManagerProvider.originalTrustProviderAlgorithm)) will
|
||||
// allow access to the original implementation which is normally "PKIX"
|
||||
Security.setProperty("ssl.TrustManagerFactory.algorithm", "whitelistTrustManager")
|
||||
}
|
||||
|
||||
/**
|
||||
@ -46,16 +53,16 @@ object WhitelistTrustManagerProvider : Provider("WhitelistTrustManager",
|
||||
// Add ourselves to whitelist as currently we have to connect to a local ArtemisMQ broker
|
||||
val host = InetAddress.getLocalHost()
|
||||
addWhitelistEntry(host.hostName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Security provider registration function for WhitelistTrustManagerProvider
|
||||
*/
|
||||
fun register() {
|
||||
Security.addProvider(WhitelistTrustManagerProvider)
|
||||
|
||||
// Register our custom TrustManagerFactorySpi
|
||||
put("TrustManagerFactory.whitelistTrustManager", "com.r3corda.core.crypto.WhitelistTrustManagerSpi")
|
||||
|
||||
// Forcibly change the TrustManagerFactory defaultAlgorithm to be us
|
||||
// This will apply to all code using TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||
// Which includes the standard HTTPS implementation and most other SSL code
|
||||
// TrustManagerFactory.getInstance(WhitelistTrustManagerProvider.originalTrustProviderAlgorithm)) will
|
||||
// allow access to the original implementation which is normally "PKIX"
|
||||
Security.setProperty("ssl.TrustManagerFactory.algorithm", "whitelistTrustManager")
|
||||
}
|
||||
|
||||
/**
|
||||
|
96
node/src/main/kotlin/com/r3corda/node/Main.kt
Normal file
96
node/src/main/kotlin/com/r3corda/node/Main.kt
Normal file
@ -0,0 +1,96 @@
|
||||
package com.r3corda.node
|
||||
|
||||
import com.r3corda.node.services.config.FullNodeConfiguration
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigRenderOptions
|
||||
import joptsimple.OptionParser
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.File
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.net.InetAddress
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
|
||||
val log = LoggerFactory.getLogger("Main")
|
||||
|
||||
object ParamsSpec {
|
||||
val parser = OptionParser()
|
||||
|
||||
// The intent of allowing a command line configurable directory and config path is to allow deployment flexibility.
|
||||
// Other general configuration should live inside the config file unless we regularly need temporary overrides on the command line
|
||||
val baseDirectoryArg =
|
||||
parser.accepts("base-directory", "The directory to put all files under")
|
||||
.withOptionalArg()
|
||||
val configFileArg =
|
||||
parser.accepts("config-file", "The path to the config file")
|
||||
.withOptionalArg()
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
log.info("Starting Corda Node")
|
||||
val cmdlineOptions = try {
|
||||
ParamsSpec.parser.parse(*args)
|
||||
} catch (ex: Exception) {
|
||||
log.error("Unable to parse args", ex)
|
||||
System.exit(1)
|
||||
return
|
||||
}
|
||||
|
||||
val baseDirectoryPath = if (cmdlineOptions.has(ParamsSpec.baseDirectoryArg)) Paths.get(cmdlineOptions.valueOf(ParamsSpec.baseDirectoryArg)) else Paths.get(".").normalize()
|
||||
|
||||
val defaultConfig = ConfigFactory.parseResources("reference.conf")
|
||||
|
||||
val configFile = if (cmdlineOptions.has(ParamsSpec.configFileArg)) {
|
||||
File(cmdlineOptions.valueOf(ParamsSpec.configFileArg))
|
||||
} else {
|
||||
baseDirectoryPath.resolve("node.conf").normalize().toFile()
|
||||
}
|
||||
val appConfig = ConfigFactory.parseFile(configFile)
|
||||
|
||||
val cmdlineOverrideMap = HashMap<String, Any?>() // If we do require a few other command line overrides eg for a nicer development experience they would go inside this map.
|
||||
if (cmdlineOptions.has(ParamsSpec.baseDirectoryArg)) {
|
||||
cmdlineOverrideMap.put("basedir", baseDirectoryPath.toString())
|
||||
}
|
||||
val overrideConfig = ConfigFactory.parseMap(cmdlineOverrideMap)
|
||||
|
||||
val mergedAndResolvedConfig = overrideConfig.withFallback(appConfig).withFallback(defaultConfig).resolve()
|
||||
|
||||
log.info("config:\n ${mergedAndResolvedConfig.root().render(ConfigRenderOptions.defaults())}")
|
||||
val conf = FullNodeConfiguration(mergedAndResolvedConfig)
|
||||
val dir = conf.basedir.toAbsolutePath().normalize()
|
||||
logInfo(args, dir)
|
||||
|
||||
try {
|
||||
val dirFile = dir.toFile()
|
||||
if (!dirFile.exists()) {
|
||||
dirFile.mkdirs()
|
||||
}
|
||||
|
||||
val node = conf.createNode()
|
||||
node.start()
|
||||
try {
|
||||
// TODO create a proper daemon and/or provide some console handler to give interactive commands
|
||||
while (true) Thread.sleep(Long.MAX_VALUE)
|
||||
} catch(e: InterruptedException) {
|
||||
node.stop()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
log.error("Exception during node startup", e)
|
||||
System.exit(1)
|
||||
}
|
||||
System.exit(0)
|
||||
}
|
||||
|
||||
private fun logInfo(args: Array<String>, dir: Path?) {
|
||||
log.info("Main class: ${FullNodeConfiguration::class.java.protectionDomain.codeSource.location.toURI().getPath()}")
|
||||
val info = ManagementFactory.getRuntimeMXBean()
|
||||
log.info("CommandLine Args: ${info.getInputArguments().joinToString(" ")}")
|
||||
log.info("Application Args: ${args.joinToString(" ")}")
|
||||
log.info("bootclasspath: ${info.bootClassPath}")
|
||||
log.info("classpath: ${info.classPath}")
|
||||
log.info("VM ${info.vmName} ${info.vmVendor} ${info.vmVersion}")
|
||||
log.info("Machine: ${InetAddress.getLocalHost().hostName}")
|
||||
log.info("Working Directory: ${dir}")
|
||||
}
|
||||
|
@ -1,8 +1,24 @@
|
||||
package com.r3corda.node.services.config
|
||||
|
||||
import com.google.common.net.HostAndPort
|
||||
import com.r3corda.core.crypto.Party
|
||||
import com.r3corda.core.crypto.generateKeyPair
|
||||
import com.r3corda.core.node.NodeInfo
|
||||
import com.r3corda.core.node.services.ServiceType
|
||||
import com.r3corda.node.internal.Node
|
||||
import com.r3corda.node.serialization.NodeClock
|
||||
import com.r3corda.node.services.messaging.ArtemisMessagingClient
|
||||
import com.r3corda.node.services.network.NetworkMapService
|
||||
import com.r3corda.node.services.transactions.SimpleNotaryService
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaType
|
||||
|
||||
interface NodeConfiguration {
|
||||
val myLegalName: String
|
||||
@ -12,8 +28,21 @@ interface NodeConfiguration {
|
||||
val trustStorePassword: String
|
||||
}
|
||||
|
||||
// Allow the use of "String by config" syntax. TODO: Make it more flexible.
|
||||
operator fun Config.getValue(receiver: NodeConfigurationFromConfig, metadata: KProperty<*>) = getString(metadata.name)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
operator fun <T> Config.getValue(receiver: Any, metadata: KProperty<*>): T {
|
||||
return when (metadata.returnType.javaType) {
|
||||
String::class.java -> getString(metadata.name) as T
|
||||
Int::class.java -> getInt(metadata.name) as T
|
||||
Long::class.java -> getLong(metadata.name) as T
|
||||
Double::class.java -> getDouble(metadata.name) as T
|
||||
Boolean::class.java -> getBoolean(metadata.name) as T
|
||||
LocalDate::class.java -> LocalDate.parse(getString(metadata.name)) as T
|
||||
Instant::class.java -> Instant.parse(getString(metadata.name)) as T
|
||||
HostAndPort::class.java -> HostAndPort.fromString(getString(metadata.name)) as T
|
||||
Path::class.java -> Paths.get(getString(metadata.name)) as T
|
||||
else -> throw IllegalArgumentException("Unsupported type ${metadata.returnType}")
|
||||
}
|
||||
}
|
||||
|
||||
class NodeConfigurationFromConfig(val config: Config = ConfigFactory.load()) : NodeConfiguration {
|
||||
override val myLegalName: String by config
|
||||
@ -21,4 +50,50 @@ class NodeConfigurationFromConfig(val config: Config = ConfigFactory.load()) : N
|
||||
override val nearestCity: String by config
|
||||
override val keyStorePassword: String by config
|
||||
override val trustStorePassword: String by config
|
||||
}
|
||||
|
||||
class NameServiceConfig(conf: Config) {
|
||||
val hostServiceLocally: Boolean by conf
|
||||
val address: HostAndPort by conf
|
||||
val identity: String by conf
|
||||
}
|
||||
|
||||
class FullNodeConfiguration(conf: Config) : NodeConfiguration {
|
||||
val basedir: Path by conf
|
||||
override val myLegalName: String by conf
|
||||
override val nearestCity: String by conf
|
||||
override val exportJMXto: String = "http"
|
||||
override val keyStorePassword: String by conf
|
||||
override val trustStorePassword: String by conf
|
||||
val artemisAddress: HostAndPort by conf
|
||||
val webAddress: HostAndPort by conf
|
||||
val messagingServerAddress: HostAndPort? = if (conf.hasPath("messagingServerAddress")) HostAndPort.fromString(conf.getString("messagingServerAddress")) else null
|
||||
val hostNotaryServiceLocally: Boolean by conf
|
||||
val extraAdvertisedServiceIds: String by conf
|
||||
val mapService: NameServiceConfig = NameServiceConfig(conf.getConfig("mapService"))
|
||||
val clock: Clock = NodeClock()
|
||||
|
||||
fun createNode(): Node {
|
||||
val networkMapTarget = ArtemisMessagingClient.makeRecipient(mapService.address)
|
||||
val advertisedServices = mutableSetOf<ServiceType>()
|
||||
if (mapService.hostServiceLocally) advertisedServices.add(NetworkMapService.Type)
|
||||
if (hostNotaryServiceLocally) advertisedServices.add(SimpleNotaryService.Type)
|
||||
if (!extraAdvertisedServiceIds.isNullOrEmpty()) {
|
||||
for (serviceId in extraAdvertisedServiceIds.split(",")) {
|
||||
advertisedServices.add(object : ServiceType(serviceId) {})
|
||||
}
|
||||
}
|
||||
// TODO Node startup should not need a full NodeInfo for the remote NetworkMapService provider as bootstrap
|
||||
val networkMapBootstrapIdentity = Party(mapService.identity, generateKeyPair().public)
|
||||
val networkMapAddress: NodeInfo? = if (mapService.hostServiceLocally) null else NodeInfo(networkMapTarget, networkMapBootstrapIdentity, setOf(NetworkMapService.Type))
|
||||
return Node(basedir.toAbsolutePath().normalize(),
|
||||
artemisAddress,
|
||||
webAddress,
|
||||
this,
|
||||
networkMapAddress,
|
||||
advertisedServices,
|
||||
clock,
|
||||
messagingServerAddress
|
||||
)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user