Merge pull request #277 from corda/clint-webservermodule

Web server is now a separate gradle submodule
This commit is contained in:
Clinton 2017-02-27 17:08:47 +00:00 committed by GitHub
commit 9ea1f09ae8
41 changed files with 356 additions and 155 deletions

View File

@ -113,6 +113,7 @@ dependencies {
compile project(':node')
compile "com.google.guava:guava:$guava_version"
runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
runtime project(path: ":node:webserver", configuration: 'runtimeArtifacts')
}
task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) {
@ -178,7 +179,7 @@ bintrayConfig {
projectUrl = 'https://github.com/corda/corda'
gpgSign = true
gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE')
publications = ['client', 'core', 'corda', 'finance', 'node', 'node-schemas', 'test-utils']
publications = ['client', 'core', 'corda', 'corda-webserver', 'finance', 'node', 'node-schemas', 'test-utils']
license {
name = 'Apache-2.0'
url = 'https://www.apache.org/licenses/LICENSE-2.0'

View File

@ -118,7 +118,7 @@ interface CordaRPCOps : RPCOps {
*/
fun uploadAttachment(jar: InputStream): SecureHash
@Suppress("DEPRECATION")
// TODO: Remove this from the interface
@Deprecated("This service will be removed in a future milestone")
fun uploadFile(dataType: String, name: String?, file: InputStream): String

View File

@ -49,6 +49,7 @@ dependencies {
}
runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
runtime project(path: ":node:webserver", configuration: 'runtimeArtifacts')
}
mainClassName = "net.corda.docs.ClientRpcTutorialKt"

View File

@ -67,13 +67,20 @@ class Cordform extends DefaultTask {
protected void installRunScript() {
project.copy {
from Cordformation.getPluginFile(project, "net/corda/plugins/runnodes")
from Cordformation.getPluginFile(project, "net/corda/plugins/runnodes.bat")
filter { String line -> line.replace("JAR_NAME", Node.JAR_NAME) }
filter { String line -> line.replace("NODEJAR_NAME", Node.NODEJAR_NAME) }
filter { String line -> line.replace("WEBJAR_NAME", Node.WEBJAR_NAME) }
// Replaces end of line with lf to avoid issues with the bash interpreter and Windows style line endings.
filter(FixCrLfFilter.class, eol: FixCrLfFilter.CrLf.newInstance("lf"))
fileMode 0755
into "${directory}/"
}
project.copy {
from Cordformation.getPluginFile(project, "net/corda/plugins/runnodes.bat")
filter { String line -> line.replace("NODEJAR_NAME", Node.NODEJAR_NAME) }
filter { String line -> line.replace("WEBJAR_NAME", Node.WEBJAR_NAME) }
into "${directory}/"
}
}
/**

View File

@ -9,7 +9,8 @@ import java.nio.file.Files
* Represents a node that will be installed.
*/
class Node {
static final String JAR_NAME = 'corda.jar'
static final String NODEJAR_NAME = 'corda.jar'
static final String WEBJAR_NAME = 'corda-webserver.jar'
static final String DEFAULT_HOST = 'localhost'
/**
@ -128,7 +129,8 @@ class Node {
void build(File rootDir) {
nodeDir = new File(rootDir, name.replaceAll("\\s",""))
configureRpcUsers()
installCordaJAR()
installCordaJar()
installWebserverJar()
installBuiltPlugin()
installCordapps()
installDependencies()
@ -154,12 +156,24 @@ class Node {
/**
* Installs the corda fat JAR to the node directory.
*/
private void installCordaJAR() {
private void installCordaJar() {
def cordaJar = verifyAndGetCordaJar()
project.copy {
from cordaJar
into nodeDir
rename cordaJar.name, JAR_NAME
rename cordaJar.name, NODEJAR_NAME
}
}
/**
* Installs the corda webserver JAR to the node directory
*/
private void installWebserverJar() {
def webJar = verifyAndGetWebserverJar()
project.copy {
from webJar
into nodeDir
rename webJar.name, WEBJAR_NAME
}
}
@ -191,10 +205,11 @@ class Node {
*/
private void installDependencies() {
def cordaJar = verifyAndGetCordaJar()
def webJar = verifyAndGetWebserverJar()
def depsDir = new File(nodeDir, "dependencies")
def coreDeps = project.zipTree(cordaJar).getFiles().collect { it.getName() }
def appDeps = project.configurations.runtime.filter {
it != cordaJar && !project.configurations.cordapp.contains(it) && !coreDeps.contains(it.getName())
(it != cordaJar) && (it != webJar) && !project.configurations.cordapp.contains(it) && !coreDeps.contains(it.getName())
}
project.copy {
from appDeps
@ -242,6 +257,24 @@ class Node {
}
}
/**
* Find the corda JAR amongst the dependencies
*
* @return A file representing the Corda webserver JAR
*/
private File verifyAndGetWebserverJar() {
def maybeJar = project.configurations.runtime.filter {
it.toString().contains("corda-webserver-${project.corda_version}.jar")
}
if (maybeJar.size() == 0) {
throw new RuntimeException("No Corda Webserver JAR found. Have you deployed the Corda project to Maven? Looked for \"corda-webserver-${project.corda_version}.jar\"")
} else {
def jar = maybeJar.getSingleFile()
assert(jar.isFile())
return jar
}
}
/**
* Gets a list of cordapps based on what dependent cordapps were specified.
*

View File

@ -16,7 +16,7 @@ if which osascript >/dev/null; then
rootdir=`pwd`
for dir in `ls`; do
if [ -d $dir ]; then
cmd="bash -c 'cd $rootdir/$dir; /usr/libexec/java_home -v 1.8 --exec java -jar JAR_NAME && exit'"
cmd="bash -c 'cd $rootdir/$dir; /usr/libexec/java_home -v 1.8 --exec java -jar NODEJAR_NAME && exit'"
script="$script
tell application \"System Events\" to tell process \"Terminal\" to keystroke \"t\" using command down
delay 0.5
@ -26,7 +26,7 @@ if which osascript >/dev/null; then
done
for dir in `ls`; do
if [ -d $dir ]; then
cmd="bash -c 'cd $rootdir/$dir; /usr/libexec/java_home -v 1.8 --exec java -jar JAR_NAME --webserver && exit'"
cmd="bash -c 'cd $rootdir/$dir; /usr/libexec/java_home -v 1.8 --exec java -jar WEBJAR_NAME && exit'"
script="$script
tell application \"System Events\" to tell process \"Terminal\" to keystroke \"t\" using command down
delay 0.5
@ -48,8 +48,8 @@ else
for dir in `ls`; do
if [ -d $dir ]; then
pushd $dir >/dev/null
xterm -T "`basename $dir`" -e 'java -jar JAR_NAME' &
xterm -T "`basename $dir` Web Server" -e 'java -jar JAR_NAME --webserver' &
xterm -T "`basename $dir`" -e 'java -jar NODEJAR_NAME' &
xterm -T "`basename $dir` Web Server" -e 'java -jar WEBJAR_NAME' &
popd >/dev/null
fi
done

View File

@ -5,8 +5,8 @@ Pushd %~dp0
FOR /D %%G in (.\*) DO (
Pushd %%G
start java -jar corda.jar
start java -jar corda.jar --webserver
start java -jar NODEJAR_NAME
start java -jar WEBJAR_NAME
Popd
)

View File

@ -9,4 +9,8 @@ class ProjectPublishExtension {
* True when we do not want to publish default Java components
*/
Boolean disableDefaultJar = false
/**
* True if publishing a WAR instead of a JAR. Forces disableDefaultJAR to "true" when true
*/
Boolean publishWar = false
}

View File

@ -60,8 +60,10 @@ class PublishTasks implements Plugin<Project> {
void configureMavenPublish(BintrayConfigExtension bintrayConfig) {
project.apply([plugin: 'maven-publish'])
project.publishing.publications.create(publishName, MavenPublication) {
if(!publishConfig.disableDefaultJar) {
if(!publishConfig.disableDefaultJar && !publishConfig.publishWar) {
from project.components.java
} else if(publishConfig.publishWar) {
from project.components.web
}
groupId project.group
artifactId publishName

View File

@ -88,30 +88,12 @@ dependencies {
exclude group: "asm"
}
// Force commons logging to version 1.2 to override Artemis, which pulls in 1.1.3 (ARTEMIS-424)
compile "commons-logging:commons-logging:1.2"
// Web stuff: for HTTP[S] servlets
compile "org.eclipse.jetty:jetty-servlet:${jetty_version}"
compile "org.eclipse.jetty:jetty-webapp:${jetty_version}"
compile "javax.servlet:javax.servlet-api:3.1.0"
compile "org.jolokia:jolokia-agent-war:$jolokia_version"
// For adding serialisation of file upload streams to RPC
// TODO: Remove this dependency and the code that requires it
compile "commons-fileupload:commons-fileupload:1.3.2"
// Jersey for JAX-RS implementation for use in Jetty
compile "org.glassfish.jersey.core:jersey-server:${jersey_version}"
compile "org.glassfish.jersey.containers:jersey-container-servlet-core:${jersey_version}"
compile "org.glassfish.jersey.containers:jersey-container-jetty-http:${jersey_version}"
// NOTE there is a Jackson version clash between jersey-media-json-jackson (v2.5.4) and jackson-module-kotlin (v.2.5.5)
// Have not found an Issue in the issue tracker for Jersey for this issue
compile ("org.glassfish.jersey.media:jersey-media-json-jackson:${jersey_version}") {
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-annotations'
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-databind'
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-core'
}
compile ("com.fasterxml.jackson.module:jackson-module-kotlin:${jackson_version}") {
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-annotations'
}
// Force commons logging to version 1.2 to override Artemis, which pulls in 1.1.3 (ARTEMIS-424)
compile "commons-logging:commons-logging:1.2"
compile "com.fasterxml.jackson.core:jackson-annotations:${jackson_version}"
// Coda Hale's Metrics: for monitoring of key statistics

View File

@ -26,14 +26,6 @@ class DriverTests {
// Check that the port is bound
addressMustNotBeBound(executorService, hostAndPort)
}
fun webserverMustBeUp(webserverAddr: HostAndPort) {
addressMustBeBound(executorService, webserverAddr)
}
fun webserverMustBeDown(webserverAddr: HostAndPort) {
addressMustNotBeBound(executorService, webserverAddr)
}
}
@Test
@ -69,15 +61,4 @@ class DriverTests {
}
nodeMustBeDown(nodeInfo.nodeInfo)
}
@Test
fun `starting a node and independent web server works`() {
val addr = driver {
val node = startNode("test").getOrThrow()
val webserverAddr = startWebserver(node).getOrThrow()
webserverMustBeUp(webserverAddr)
webserverAddr
}
webserverMustBeDown(addr)
}
}

View File

@ -28,7 +28,6 @@ class ArgsParser {
.withValuesConvertedBy(object : EnumConverter<Level>(Level::class.java) {})
.defaultsTo(Level.INFO)
private val logToConsoleArg = optionParser.accepts("log-to-console", "If set, prints logging to the console as well as to a file.")
private val isWebserverArg = optionParser.accepts("webserver")
private val isRegistrationArg = optionParser.accepts("initial-registration", "Start initial node registration with Corda network to obtain certificate from the permissioning server.")
private val helpArg = optionParser.accepts("help").forHelp()
@ -42,9 +41,8 @@ class ArgsParser {
val help = optionSet.has(helpArg)
val loggingLevel = optionSet.valueOf(loggerLevel)
val logToConsole = optionSet.has(logToConsoleArg)
val isWebserver = optionSet.has(isWebserverArg)
val isRegistration = optionSet.has(isRegistrationArg)
return CmdLineOptions(baseDirectory, configFile, help, loggingLevel, logToConsole, isWebserver, isRegistration)
return CmdLineOptions(baseDirectory, configFile, help, loggingLevel, logToConsole, isRegistration)
}
fun printHelp(sink: PrintStream) = optionParser.printHelpOn(sink)
@ -55,9 +53,8 @@ data class CmdLineOptions(val baseDirectory: Path,
val help: Boolean,
val loggingLevel: Level,
val logToConsole: Boolean,
val isWebserver: Boolean,
val isRegistration: Boolean) {
fun loadConfig(allowMissingConfig: Boolean = false, configOverrides: Map<String, Any?> = emptyMap()): Config {
return ConfigHelper.loadConfig(baseDirectory, configFile, allowMissingConfig, configOverrides)
}
}
}

View File

@ -10,7 +10,6 @@ import net.corda.node.services.config.FullNodeConfiguration
import net.corda.node.utilities.ANSIProgressObserver
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
import net.corda.node.utilities.registration.NetworkRegistrationHelper
import net.corda.node.webserver.WebServer
import org.fusesource.jansi.Ansi
import org.fusesource.jansi.AnsiConsole
import org.slf4j.LoggerFactory
@ -61,8 +60,7 @@ fun main(args: Array<String>) {
drawBanner()
val logDir = if (cmdlineOptions.isWebserver) "logs/web" else "logs"
System.setProperty("log-path", (cmdlineOptions.baseDirectory / logDir).toString())
System.setProperty("log-path", (cmdlineOptions.baseDirectory / "logs").toString())
val log = LoggerFactory.getLogger("Main")
printBasicNodeInfo("Logs can be found in", System.getProperty("log-path"))
@ -94,39 +92,26 @@ fun main(args: Array<String>) {
log.info("VM ${info.vmName} ${info.vmVendor} ${info.vmVersion}")
log.info("Machine: ${InetAddress.getLocalHost().hostName}")
log.info("Working Directory: ${cmdlineOptions.baseDirectory}")
if (cmdlineOptions.isWebserver) {
log.info("Starting as webserver on ${conf.webAddress}")
} else {
log.info("Starting as node on ${conf.artemisAddress}")
}
log.info("Starting as node on ${conf.artemisAddress}")
try {
cmdlineOptions.baseDirectory.createDirectories()
// TODO: Webserver should be split and start from inside a WAR container
if (!cmdlineOptions.isWebserver) {
val node = conf.createNode()
node.start()
printPluginsAndServices(node)
val node = conf.createNode()
node.start()
printPluginsAndServices(node)
node.networkMapRegistrationFuture.success {
val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0
printBasicNodeInfo("Node started up and registered in $elapsed sec")
if (renderBasicInfoToConsole)
ANSIProgressObserver(node.smm)
} failure {
log.error("Error during network map registration", it)
exitProcess(1)
}
node.run()
} else {
val server = WebServer(conf)
server.start()
node.networkMapRegistrationFuture.success {
val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0
printBasicNodeInfo("Webserver started up in $elapsed sec")
server.run()
printBasicNodeInfo("Node started up and registered in $elapsed sec")
if (renderBasicInfoToConsole)
ANSIProgressObserver(node.smm)
} failure {
log.error("Error during network map registration", it)
exitProcess(1)
}
node.run()
} catch (e: Exception) {
log.error("Exception during node startup", e)
exitProcess(1)

View File

@ -420,7 +420,7 @@ open class DriverDSL(
}
}
private fun queryWebserver(configuration: FullNodeConfiguration): HostAndPort? {
private fun queryWebserver(configuration: FullNodeConfiguration, process: Process): HostAndPort? {
val protocol = if (configuration.useHTTPS) {
"https://"
} else {
@ -429,7 +429,7 @@ open class DriverDSL(
val url = URL(protocol + configuration.webAddress.toString() + "/api/status")
val client = OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(60, TimeUnit.SECONDS).build()
while (true) try {
while (process.isAlive) try {
val response = client.newCall(Request.Builder().url(url).build()).execute()
if (response.isSuccessful && (response.body().string() == "started")) {
return configuration.webAddress
@ -437,14 +437,17 @@ open class DriverDSL(
} catch(e: ConnectException) {
log.debug("Retrying webserver info at ${configuration.webAddress}")
}
log.error("Webserver at ${configuration.webAddress} has died")
return null
}
override fun startWebserver(handle: NodeHandle): ListenableFuture<HostAndPort> {
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
return future {
registerProcess(DriverDSL.startWebserver(executorService, handle.configuration, debugPort))
queryWebserver(handle.configuration)!!
val process = DriverDSL.startWebserver(executorService, handle.configuration, debugPort)
registerProcess(process)
return process.map {
queryWebserver(handle.configuration, it)!!
}
}
@ -542,7 +545,7 @@ open class DriverDSL(
executorService: ScheduledExecutorService,
nodeConf: FullNodeConfiguration,
debugPort: Int?): ListenableFuture<Process> {
val className = "net.corda.node.Corda" // cannot directly get class for this, so just use string
val className = "net.corda.webserver.WebServer" // cannot directly get class for this, so just use string
val separator = System.getProperty("file.separator")
val classpath = System.getProperty("java.class.path")
val path = System.getProperty("java.home") + separator + "bin" + separator + "java"
@ -556,8 +559,7 @@ open class DriverDSL(
listOf("-Dname=node-${nodeConf.artemisAddress}-webserver") + debugPortArg +
listOf(
"-cp", classpath, className,
"--base-directory", nodeConf.baseDirectory.toString(),
"--webserver")
"--base-directory", nodeConf.baseDirectory.toString())
val builder = ProcessBuilder(javaArgs)
builder.redirectError(Paths.get("error.$className.log").toFile())
builder.inheritIO()

View File

@ -33,7 +33,6 @@ import java.nio.file.Files
import java.nio.file.Paths
import java.time.Clock
import javax.management.ObjectName
import javax.servlet.*
import kotlin.concurrent.thread
/**
@ -288,18 +287,6 @@ class Node(override val configuration: FullNodeConfiguration,
f.setLength(0)
f.write(ourProcessID.toByteArray())
}
// Servlet filter to wrap API requests with a database transaction.
private class DatabaseTransactionFilter(val database: Database) : Filter {
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
databaseTransaction(database) {
chain.doFilter(request, response)
}
}
override fun init(filterConfig: FilterConfig?) {}
override fun destroy() {}
}
}
class ConfigurationException(message: String) : Exception(message)

View File

@ -20,7 +20,6 @@ class ArgsParserTest {
help = false,
logToConsole = false,
loggingLevel = Level.INFO,
isWebserver = false,
isRegistration = false))
}
@ -69,12 +68,6 @@ class ArgsParserTest {
}
}
@Test
fun `webserver`() {
val cmdLineOptions = parser.parse("--webserver")
assertThat(cmdLineOptions.isWebserver).isTrue()
}
@Test
fun `both base-directory and config-file`() {
assertThatExceptionOfType(IllegalArgumentException::class.java).isThrownBy {
@ -109,4 +102,4 @@ class ArgsParserTest {
parser.parse("--logging-level", "not-a-level")
}.withMessageContaining("logging-level")
}
}
}

115
node/webserver/build.gradle Normal file
View File

@ -0,0 +1,115 @@
apply plugin: 'kotlin'
apply plugin: 'java'
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'us.kirchmeier.capsule'
// TODO: Break dependency on node and move to another location such as a submodule of client.
description 'Corda webserver module'
repositories {
mavenLocal()
mavenCentral()
jcenter()
maven {
url 'https://dl.bintray.com/kotlin/exposed'
}
}
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
runtimeArtifacts
}
sourceSets {
integrationTest {
kotlin {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/kotlin')
}
}
test {
resources {
srcDir "../config/test"
}
}
main {
resources {
srcDir "../config/dev"
}
}
}
dependencies {
compile project(':core')
compile project(':node') // TODO: Break this dependency
// Web stuff: for HTTP[S] servlets
compile "org.eclipse.jetty:jetty-servlet:${jetty_version}"
compile "org.eclipse.jetty:jetty-webapp:${jetty_version}"
compile "javax.servlet:javax.servlet-api:3.1.0"
compile "org.jolokia:jolokia-agent-war:$jolokia_version"
compile "commons-fileupload:commons-fileupload:1.3.2"
// Jersey for JAX-RS implementation for use in Jetty
compile "org.glassfish.jersey.core:jersey-server:${jersey_version}"
compile "org.glassfish.jersey.containers:jersey-container-servlet-core:${jersey_version}"
compile "org.glassfish.jersey.containers:jersey-container-jetty-http:${jersey_version}"
// NOTE there is a Jackson version clash between jersey-media-json-jackson (v2.5.4) and jackson-module-kotlin (v.2.5.5)
// Have not found an Issue in the issue tracker for Jersey for this issue
compile ("org.glassfish.jersey.media:jersey-media-json-jackson:${jersey_version}") {
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-annotations'
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-databind'
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-core'
}
compile ("com.fasterxml.jackson.module:jackson-module-kotlin:${jackson_version}") {
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-annotations'
}
testCompile "junit:junit:$junit_version"
}
task integrationTest(type: Test) {
testClassesDir = sourceSets.integrationTest.output.classesDir
classpath = sourceSets.integrationTest.runtimeClasspath
}
publish {
name = 'corda-webserver'
publishWar = false // TODO: Use WAR instead of JAR
}
task buildWebserverJar(type: FatCapsule) {
applicationClass 'net.corda.webserver.WebServer'
archiveName "corda-webserver-${corda_version}.jar"
applicationSource = files(project.tasks.findByName('jar'), '../build/classes/main/CordaCaplet.class', 'config/dev/log4j2.xml')
from 'NOTICE' // Copy CDDL notice
capsuleManifest {
applicationVersion = corda_version
javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"]
systemProperties['visualvm.display.name'] = 'Corda Webserver'
systemProperties['corda.version'] = corda_version
minJavaVersion = '1.8.0'
// This version is known to work and avoids earlier 8u versions that have bugs.
minUpdateVersion['1.8'] = '102'
caplets = ['CordaCaplet']
// JVM configuration:
// - Constrain to small heap sizes to ease development on low end devices.
// - Switch to the G1 GC which is going to be the default in Java 9 and gives low pause times/string dedup.
//
// If you change these flags, please also update Driver.kt
jvmArgs = ['-Xmx200m', '-XX:+UseG1GC']
}
manifest {
attributes('Corda-Version': corda_version)
}
}
artifacts {
runtimeArtifacts buildWebserverJar
}

View File

@ -0,0 +1,34 @@
package net.corda.webserver
import com.google.common.net.HostAndPort
import net.corda.core.getOrThrow
import net.corda.node.driver.addressMustBeBound
import net.corda.node.driver.addressMustNotBeBound
import net.corda.node.driver.driver
import org.junit.Test
import java.util.concurrent.Executors
class DriverTests {
companion object {
val executorService = Executors.newScheduledThreadPool(2)
fun webserverMustBeUp(webserverAddr: HostAndPort) {
addressMustBeBound(executorService, webserverAddr)
}
fun webserverMustBeDown(webserverAddr: HostAndPort) {
addressMustNotBeBound(executorService, webserverAddr)
}
}
@Test
fun `starting a node and independent web server works`() {
val addr = driver {
val node = startNode("test").getOrThrow()
val webserverAddr = startWebserver(node).getOrThrow()
webserverMustBeUp(webserverAddr)
webserverAddr
}
webserverMustBeDown(addr)
}
}

View File

@ -0,0 +1,71 @@
@file:JvmName("WebServer")
package net.corda.webserver
import com.typesafe.config.ConfigException
import net.corda.core.div
import net.corda.core.rootCause
import net.corda.node.ArgsParser
import net.corda.node.services.config.FullNodeConfiguration
import net.corda.webserver.internal.NodeWebServer
import org.slf4j.LoggerFactory
import java.lang.management.ManagementFactory
import java.net.InetAddress
import kotlin.system.exitProcess
fun main(args: Array<String>) {
val startTime = System.currentTimeMillis()
val argsParser = ArgsParser()
val cmdlineOptions = try {
argsParser.parse(*args)
} catch (ex: Exception) {
println("Unknown command line arguments: ${ex.message}")
exitProcess(1)
}
// Maybe render command line help.
if (cmdlineOptions.help) {
argsParser.printHelp(System.out)
exitProcess(0)
}
// Set up logging.
if (cmdlineOptions.logToConsole) {
// This property is referenced from the XML config file.
System.setProperty("consoleLogLevel", "info")
}
System.setProperty("log-path", (cmdlineOptions.baseDirectory / "web/logs").toString())
val log = LoggerFactory.getLogger("Main")
println("Logs can be found in ${System.getProperty("log-path")}")
val conf = try {
FullNodeConfiguration(cmdlineOptions.baseDirectory, cmdlineOptions.loadConfig())
} catch (e: ConfigException) {
println("Unable to load the configuration file: ${e.rootCause.message}")
exitProcess(2)
}
log.info("Main class: ${FullNodeConfiguration::class.java.protectionDomain.codeSource.location.toURI().path}")
val info = ManagementFactory.getRuntimeMXBean()
log.info("CommandLine Args: ${info.inputArguments.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: ${cmdlineOptions.baseDirectory}")
log.info("Starting as webserver on ${conf.webAddress}")
try {
val server = NodeWebServer(conf)
server.start()
val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0
println("Webserver started up in $elapsed sec")
server.run()
} catch (e: Exception) {
log.error("Exception during node startup", e)
exitProcess(1)
}
}

View File

@ -1,4 +1,4 @@
package net.corda.node.webserver.api
package net.corda.webserver.api
import net.corda.core.node.NodeInfo
import java.time.LocalDateTime

View File

@ -1,4 +1,4 @@
package net.corda.node.webserver.api
package net.corda.webserver.api
/**
* Extremely rudimentary query language which should most likely be replaced with a product.

View File

@ -1,7 +1,7 @@
package net.corda.node.webserver.internal
package net.corda.webserver.internal
import net.corda.core.messaging.CordaRPCOps
import net.corda.node.webserver.api.*
import net.corda.webserver.api.*
import java.time.LocalDateTime
import java.time.ZoneId
import javax.ws.rs.core.Response

View File

@ -1,4 +1,4 @@
package net.corda.node.webserver
package net.corda.webserver.internal
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.node.CordaPluginRegistry
@ -7,11 +7,10 @@ import net.corda.node.printBasicNodeInfo
import net.corda.node.services.config.FullNodeConfiguration
import net.corda.node.services.messaging.ArtemisMessagingComponent
import net.corda.node.services.messaging.CordaRPCClient
import net.corda.node.webserver.internal.APIServerImpl
import net.corda.node.webserver.servlets.AttachmentDownloadServlet
import net.corda.node.webserver.servlets.DataUploadServlet
import net.corda.node.webserver.servlets.ObjectMapperConfig
import net.corda.node.webserver.servlets.ResponseFilter
import net.corda.webserver.servlets.AttachmentDownloadServlet
import net.corda.webserver.servlets.DataUploadServlet
import net.corda.webserver.servlets.ObjectMapperConfig
import net.corda.webserver.servlets.ResponseFilter
import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException
import org.eclipse.jetty.server.*
import org.eclipse.jetty.server.handler.HandlerCollection
@ -24,12 +23,12 @@ import org.glassfish.jersey.server.ResourceConfig
import org.glassfish.jersey.server.ServerProperties
import org.glassfish.jersey.servlet.ServletContainer
import java.lang.reflect.InvocationTargetException
import java.net.InetAddress
import java.util.*
// TODO: Split into a separate module under client that packages into WAR formats.
class WebServer(val config: FullNodeConfiguration) {
class NodeWebServer(val config: FullNodeConfiguration) {
private companion object {
val log = loggerFor<WebServer>()
val log = loggerFor<NodeWebServer>()
val retryDelay = 1000L // Milliseconds
}
@ -51,7 +50,6 @@ class WebServer(val config: FullNodeConfiguration) {
// Note that the web server handlers will all run concurrently, and not on the node thread.
val handlerCollection = HandlerCollection()
// TODO: Move back into the node itself.
// Export JMX monitoring statistics and data over REST/JSON.
if (config.exportJMXto.split(',').contains("http")) {
val classpath = System.getProperty("java.class.path").split(System.getProperty("path.separator"))
@ -101,7 +99,7 @@ class WebServer(val config: FullNodeConfiguration) {
server.handler = handlerCollection
server.start()
log.info("Started webserver on address $address")
log.info("Starting webserver on address $address")
return server
}
@ -157,9 +155,9 @@ class WebServer(val config: FullNodeConfiguration) {
} catch (e: ActiveMQNotConnectedException) {
log.debug("Could not connect to ${config.artemisAddress} due to exception: ", e)
Thread.sleep(retryDelay)
// This error will happen if the server has yet to create the keystore
// Keep the fully qualified package name due to collisions with the Kotlin stdlib
// exception of the same name
// This error will happen if the server has yet to create the keystore
// Keep the fully qualified package name due to collisions with the Kotlin stdlib
// exception of the same name
} catch (e: java.nio.file.NoSuchFileException) {
log.debug("Tried to open a file that doesn't yet exist, retrying", e)
Thread.sleep(retryDelay)

View File

@ -1,4 +1,4 @@
package net.corda.node.webserver.servlets
package net.corda.webserver.servlets
import net.corda.core.crypto.SecureHash
import net.corda.core.node.services.StorageService

View File

@ -1,9 +1,7 @@
package net.corda.node.webserver.servlets
package net.corda.webserver.servlets
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.utilities.loggerFor
import net.corda.node.internal.Node
import net.corda.node.services.api.AcceptsFileUpload
import org.apache.commons.fileupload.servlet.ServletFileUpload
import java.util.*
import javax.servlet.http.HttpServlet
@ -11,7 +9,7 @@ import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
/**
* Accepts binary streams, finds the right [AcceptsFileUpload] implementor and hands the stream off to it.
* Uploads to the node via the [CordaRPCOps] uploadFile interface.
*/
class DataUploadServlet: HttpServlet() {
private val log = loggerFor<DataUploadServlet>()
@ -41,6 +39,7 @@ class DataUploadServlet: HttpServlet() {
try {
val dataType = req.pathInfo.substring(1).substringBefore('/')
@Suppress("DEPRECATION") // TODO: Replace the use of uploadFile
messages += rpc.uploadFile(dataType, item.name, item.openStream())
log.info("${item.name} successfully accepted: ${messages.last()}")
} catch(e: RuntimeException) {

View File

@ -1,9 +1,8 @@
package net.corda.node.webserver.servlets
package net.corda.webserver.servlets
import com.fasterxml.jackson.databind.ObjectMapper
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.node.ServiceHub
import net.corda.node.utilities.JsonSupport
import net.corda.webserver.utilities.JsonSupport
import javax.ws.rs.ext.ContextResolver
import javax.ws.rs.ext.Provider

View File

@ -1,4 +1,4 @@
package net.corda.node.webserver.servlets
package net.corda.webserver.servlets
import javax.ws.rs.container.ContainerRequestContext
import javax.ws.rs.container.ContainerResponseContext

View File

@ -1,4 +1,4 @@
package net.corda.node.utilities
package net.corda.webserver.utilities
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParseException

View File

@ -1,11 +1,10 @@
package net.corda.node
package net.corda.webserver
import com.pholser.junit.quickcheck.From
import com.pholser.junit.quickcheck.Property
import com.pholser.junit.quickcheck.runner.JUnitQuickcheck
import net.corda.core.testing.PublicKeyGenerator
import net.corda.node.utilities.JsonSupport
import net.corda.testing.node.MockIdentityService
import net.corda.webserver.utilities.JsonSupport
import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.junit.runner.RunWith
import java.security.PublicKey

View File

@ -1 +1 @@
gradlePluginsVersion=0.8.2
gradlePluginsVersion=0.8.3

View File

@ -46,6 +46,7 @@ dependencies {
// Corda integration dependencies
runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
runtime project(path: ":node:webserver", configuration: 'runtimeArtifacts')
compile project(':core')
compile project(':test-utils')

View File

@ -46,6 +46,7 @@ dependencies {
// Corda integration dependencies
runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
runtime project(path: ":node:webserver", configuration: 'runtimeArtifacts')
compile project(':core')
compile project(':client')
compile project(':node')

View File

@ -49,8 +49,10 @@ dependencies {
// Corda integration dependencies
runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
runtime project(path: ":node:webserver", configuration: 'runtimeArtifacts')
compile project(':core')
compile project(':finance')
compile project(':node:webserver')
compile project(':test-utils')
// Javax is required for webapis

View File

@ -24,6 +24,7 @@ import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.MockIdentityService
import net.i2p.crypto.eddsa.KeyPairGenerator
import java.security.SecureRandom
import net.corda.webserver.utilities.JsonSupport
import java.time.LocalDate
import java.util.*
@ -32,7 +33,7 @@ import java.util.*
* A simulation in which banks execute interest rate swaps with each other, including the fixing events.
*/
class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, latencyInjector: InMemoryMessagingNetwork.LatencyCalculator?) : Simulation(networkSendManuallyPumped, runAsync, latencyInjector) {
val om = net.corda.node.utilities.JsonSupport.createInMemoryMapper(MockIdentityService(network.identities))
val om = JsonSupport.createInMemoryMapper(MockIdentityService(network.identities))
init {
currentDateAndTime = LocalDate.of(2016, 3, 8).atStartOfDay()

View File

@ -22,6 +22,7 @@ dependencies {
// Corda integration dependencies
runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
runtime project(path: ":node:webserver", configuration: 'runtimeArtifacts')
compile project(':core')
compile project(':finance')
testCompile project(':test-utils')

View File

@ -46,6 +46,7 @@ dependencies {
// Corda integration dependencies
runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
runtime project(path: ":node:webserver", configuration: 'runtimeArtifacts')
compile project(':core')
compile project(':client')
compile project(':node')

View File

@ -54,6 +54,7 @@ dependencies {
// Corda integration dependencies
runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
runtime project(path: ":node:webserver", configuration: 'runtimeArtifacts')
compile project(':core')
compile project(':node')
compile project(':finance')

View File

@ -46,6 +46,7 @@ dependencies {
// Corda integration dependencies
runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
runtime project(path: ":node:webserver", configuration: 'runtimeArtifacts')
compile project(':core')
compile project(':finance')
compile project(':test-utils')

View File

@ -7,6 +7,7 @@ include 'core'
include 'node-schemas'
include 'node'
include 'node:capsule'
include 'node:webserver'
include 'client'
include 'experimental'
include 'experimental:sandbox'

View File

@ -26,6 +26,7 @@ dependencies {
compile project(':finance')
compile project(':core')
compile project(':node')
compile project(':node:webserver')
// Log4J: logging framework (with SLF4J bindings)
compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}"

View File

@ -3,7 +3,7 @@ package net.corda.testing.http
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import net.corda.core.utilities.loggerFor
import net.corda.node.utilities.JsonSupport
import net.corda.webserver.utilities.JsonSupport
import okhttp3.*
import java.net.URL
import java.util.concurrent.TimeUnit