From 8414c97a614b1a339b5e6ce311eb3fd4560cc709 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Mon, 30 Jan 2017 14:00:54 +0000 Subject: [PATCH] Moved JsonSupport to new webserver module. Fixed a few compile errors. Fixed compile issues caused by webserver being split to a separate project. WebServer now starts and stops correctly as a separate module. --- build.gradle | 3 +- .../net/corda/core/messaging/CordaRPCOps.kt | 2 +- docs/source/example-code/build.gradle | 1 + .../main/groovy/net/corda/plugins/Node.groovy | 36 ++- .../plugins/ProjectPublishExtension.groovy | 4 + .../net/corda/plugins/PublishTasks.groovy | 4 +- node/build.gradle | 26 +- .../net/corda/node/driver/DriverTests.kt | 19 -- .../main/kotlin/net/corda/node/ArgsParser.kt | 7 +- node/src/main/kotlin/net/corda/node/Corda.kt | 43 ++-- .../kotlin/net/corda/node/driver/Driver.kt | 20 +- .../kotlin/net/corda/node/internal/Node.kt | 13 - .../kotlin/net/corda/node/ArgsParserTest.kt | 10 +- node/webserver/build.gradle | 81 ++++++- .../corda/webserver/WebserverDriverTests.kt | 34 +++ .../kotlin/net/corda/webserver/WebServer.kt | 224 +++++------------- .../net/corda/webserver/api/APIServer.kt | 2 +- .../kotlin/net/corda/webserver/api/Query.kt | 2 +- .../corda/webserver/internal/APIServerImpl.kt | 4 +- .../corda/webserver/internal/NodeWebServer.kt | 176 ++++++++++++++ .../servlets/AttachmentDownloadServlet.kt | 2 +- .../webserver/servlets/DataUploadServlet.kt | 7 +- .../webserver/servlets/ObjectMapperConfig.kt | 5 +- .../webserver/servlets/ResponseFilter.kt | 2 +- .../corda/webserver}/utilities/JsonSupport.kt | 2 +- .../net/corda/webserver}/JsonSupportTest.kt | 5 +- publish.properties | 2 +- samples/attachment-demo/build.gradle | 1 + samples/bank-of-corda-demo/build.gradle | 1 + samples/irs-demo/build.gradle | 2 + .../net/corda/simulation/IRSSimulation.kt | 3 +- samples/network-visualiser/build.gradle | 1 + samples/raft-notary-demo/build.gradle | 1 + samples/simm-valuation-demo/build.gradle | 1 + samples/trader-demo/build.gradle | 1 + test-utils/build.gradle | 1 + .../net/corda/testing/http/HttpUtils.kt | 2 +- 37 files changed, 450 insertions(+), 300 deletions(-) create mode 100644 node/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt create mode 100644 node/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt rename node/{src/main/kotlin/net/corda/node => webserver/src/main/kotlin/net/corda/webserver}/utilities/JsonSupport.kt (99%) rename node/{src/test/kotlin/net/corda/node => webserver/src/test/kotlin/net/corda/webserver}/JsonSupportTest.kt (86%) diff --git a/build.gradle b/build.gradle index 4e40901972..4340636f80 100644 --- a/build.gradle +++ b/build.gradle @@ -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' diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt index 4f4f50362a..ed9bf00604 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -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 diff --git a/docs/source/example-code/build.gradle b/docs/source/example-code/build.gradle index 7b0b7fe1cc..89cd1976ff 100644 --- a/docs/source/example-code/build.gradle +++ b/docs/source/example-code/build.gradle @@ -49,6 +49,7 @@ dependencies { } runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') + runtime project(path: ":node:webserver", configuration: 'runtimeArtifacts') } mainClassName = "net.corda.docs.ClientRpcTutorialKt" diff --git a/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Node.groovy b/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Node.groovy index 926cc138d7..b8194c8a97 100644 --- a/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Node.groovy +++ b/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Node.groovy @@ -10,6 +10,7 @@ import java.nio.file.Files */ class Node { static final String JAR_NAME = 'corda.jar' + static final String WAR_NAME = 'corda-webserver.war' 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() + installWebserverWar() installBuiltPlugin() installCordapps() installDependencies() @@ -154,7 +156,7 @@ class Node { /** * Installs the corda fat JAR to the node directory. */ - private void installCordaJAR() { + private void installCordaJar() { def cordaJar = verifyAndGetCordaJar() project.copy { from cordaJar @@ -163,6 +165,18 @@ class Node { } } + /** + * Installs the corda webserver WAR to the node directory + */ + private void installWebserverWar() { + def webWar = verifyAndGetWebserverWar() + project.copy { + from webWar + into nodeDir + rename webWar.name, WAR_NAME + } + } + /** * Installs this project's cordapp to this directory. */ @@ -242,6 +256,24 @@ class Node { } } + /** + * Find the corda WAR amongst the dependencies + * + * @return A file representing the Corda webserver WAR + */ + private File verifyAndGetWebserverWar() { + def maybeWar = project.configurations.runtime.filter { + it.toString().contains("corda-webserver-${project.corda_version}.war") + } + if (maybeWar.size() == 0) { + throw new RuntimeException("No Corda Webserver WAR found. Have you deployed the Corda project to Maven? Looked for \"corda-webserver-${project.corda_version}.war\"") + } else { + def war = maybeWar.getSingleFile() + assert(war.isFile()) + return war + } + } + /** * Gets a list of cordapps based on what dependent cordapps were specified. * diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/ProjectPublishExtension.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/ProjectPublishExtension.groovy index fbccf8b4dd..de5c4dfb90 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/ProjectPublishExtension.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/ProjectPublishExtension.groovy @@ -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 } \ No newline at end of file diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy index d94880dfe5..8dd3346432 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy @@ -60,8 +60,10 @@ class PublishTasks implements Plugin { 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 diff --git a/node/build.gradle b/node/build.gradle index dc76d5548d..e96f8f8fd2 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -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 diff --git a/node/src/integration-test/kotlin/net/corda/node/driver/DriverTests.kt b/node/src/integration-test/kotlin/net/corda/node/driver/DriverTests.kt index abd8330183..0b4c8d0210 100644 --- a/node/src/integration-test/kotlin/net/corda/node/driver/DriverTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/driver/DriverTests.kt @@ -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) - } } diff --git a/node/src/main/kotlin/net/corda/node/ArgsParser.kt b/node/src/main/kotlin/net/corda/node/ArgsParser.kt index 7decb8cc79..a4af516614 100644 --- a/node/src/main/kotlin/net/corda/node/ArgsParser.kt +++ b/node/src/main/kotlin/net/corda/node/ArgsParser.kt @@ -28,7 +28,6 @@ class ArgsParser { .withValuesConvertedBy(object : EnumConverter(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 = emptyMap()): Config { return ConfigHelper.loadConfig(baseDirectory, configFile, allowMissingConfig, configOverrides) } -} \ No newline at end of file +} diff --git a/node/src/main/kotlin/net/corda/node/Corda.kt b/node/src/main/kotlin/net/corda/node/Corda.kt index 449fb2c018..8d33cc67f4 100644 --- a/node/src/main/kotlin/net/corda/node/Corda.kt +++ b/node/src/main/kotlin/net/corda/node/Corda.kt @@ -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) { 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) { 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) diff --git a/node/src/main/kotlin/net/corda/node/driver/Driver.kt b/node/src/main/kotlin/net/corda/node/driver/Driver.kt index 5bb6f8028a..7e9081c701 100644 --- a/node/src/main/kotlin/net/corda/node/driver/Driver.kt +++ b/node/src/main/kotlin/net/corda/node/driver/Driver.kt @@ -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 { 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 { - 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() diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 43984c4466..d14fe68b8e 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -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) diff --git a/node/src/test/kotlin/net/corda/node/ArgsParserTest.kt b/node/src/test/kotlin/net/corda/node/ArgsParserTest.kt index bbaf20508d..d3c1b460b3 100644 --- a/node/src/test/kotlin/net/corda/node/ArgsParserTest.kt +++ b/node/src/test/kotlin/net/corda/node/ArgsParserTest.kt @@ -5,7 +5,6 @@ import net.corda.core.div import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.Test -import org.slf4j.event.Level import java.nio.file.Paths class ArgsParserTest { @@ -20,7 +19,6 @@ class ArgsParserTest { help = false, logToConsole = false, loggingLevel = Level.INFO, - isWebserver = false, isRegistration = false)) } @@ -69,12 +67,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 +101,4 @@ class ArgsParserTest { parser.parse("--logging-level", "not-a-level") }.withMessageContaining("logging-level") } -} \ No newline at end of file +} diff --git a/node/webserver/build.gradle b/node/webserver/build.gradle index e7b0636541..edcda1dd5a 100644 --- a/node/webserver/build.gradle +++ b/node/webserver/build.gradle @@ -1,15 +1,92 @@ apply plugin: 'kotlin' apply plugin: 'java' +apply plugin: 'war' apply plugin: 'net.corda.plugins.publish-utils' -description 'Corda node modules' +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(':node') + 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 = true +} + +war { + baseName = 'corda-webserver' + manifest { + attributes('Corda-Version': corda_version) + } +} + +artifacts { + runtimeArtifacts war } \ No newline at end of file diff --git a/node/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt b/node/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt new file mode 100644 index 0000000000..1e167aaa71 --- /dev/null +++ b/node/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt @@ -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) + } +} diff --git a/node/webserver/src/main/kotlin/net/corda/webserver/WebServer.kt b/node/webserver/src/main/kotlin/net/corda/webserver/WebServer.kt index 02814fcbdd..bc9634da43 100644 --- a/node/webserver/src/main/kotlin/net/corda/webserver/WebServer.kt +++ b/node/webserver/src/main/kotlin/net/corda/webserver/WebServer.kt @@ -1,181 +1,71 @@ -package net.corda.node.webserver +@file:JvmName("WebServer") -import net.corda.core.messaging.CordaRPCOps -import net.corda.core.node.CordaPluginRegistry -import net.corda.core.utilities.loggerFor -import net.corda.node.printBasicNodeInfo +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.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 org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException -import org.eclipse.jetty.server.* -import org.eclipse.jetty.server.handler.HandlerCollection -import org.eclipse.jetty.servlet.DefaultServlet -import org.eclipse.jetty.servlet.ServletContextHandler -import org.eclipse.jetty.servlet.ServletHolder -import org.eclipse.jetty.util.ssl.SslContextFactory -import org.eclipse.jetty.webapp.WebAppContext -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.util.* +import net.corda.webserver.internal.NodeWebServer +import org.slf4j.LoggerFactory +import java.lang.management.ManagementFactory +import java.net.InetAddress +import kotlin.system.exitProcess -// TODO: Split into a separate module under client that packages into WAR formats. -class WebServer(val config: FullNodeConfiguration) { - private companion object { - val log = loggerFor() - val retryDelay = 1000L // Milliseconds +fun main(args: Array) { + 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) } - val address = config.webAddress - private lateinit var server: Server - - fun start() { - printBasicNodeInfo("Starting as webserver: ${config.webAddress}") - server = initWebServer(retryConnectLocalRpc()) + // Maybe render command line help. + if (cmdlineOptions.help) { + argsParser.printHelp(System.out) + exitProcess(0) } - fun run() { - while (server.isRunning) { - Thread.sleep(100) // TODO: Redesign - } + // Set up logging. + if (cmdlineOptions.logToConsole) { + // This property is referenced from the XML config file. + System.setProperty("consoleLogLevel", "info") } - private fun initWebServer(localRpc: CordaRPCOps): Server { - // Note that the web server handlers will all run concurrently, and not on the node thread. - val handlerCollection = HandlerCollection() + System.setProperty("log-path", (cmdlineOptions.baseDirectory / "web/logs").toString()) + val log = LoggerFactory.getLogger("Main") + println("Logs can be found in ${System.getProperty("log-path")}") - // 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")) - val warpath = classpath.firstOrNull { it.contains("jolokia-agent-war-2") && it.endsWith(".war") } - if (warpath != null) { - handlerCollection.addHandler(WebAppContext().apply { - // Find the jolokia WAR file on the classpath. - contextPath = "/monitoring/json" - setInitParameter("mimeType", "application/json") - war = warpath - }) - } else { - log.warn("Unable to locate Jolokia WAR on classpath") - } - } + val conf = try { + FullNodeConfiguration(cmdlineOptions.baseDirectory, cmdlineOptions.loadConfig()) + } catch (e: ConfigException) { + println("Unable to load the configuration file: ${e.rootCause.message}") + exitProcess(2) + } - // API, data upload and download to services (attachments, rates oracles etc) - handlerCollection.addHandler(buildServletContextHandler(localRpc)) + 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}") - val server = Server() - - val connector = if (config.useHTTPS) { - val httpsConfiguration = HttpConfiguration() - httpsConfiguration.outputBufferSize = 32768 - httpsConfiguration.addCustomizer(SecureRequestCustomizer()) - val sslContextFactory = SslContextFactory() - sslContextFactory.keyStorePath = config.keyStoreFile.toString() - sslContextFactory.setKeyStorePassword(config.keyStorePassword) - sslContextFactory.setKeyManagerPassword(config.keyStorePassword) - sslContextFactory.setTrustStorePath(config.trustStoreFile.toString()) - sslContextFactory.setTrustStorePassword(config.trustStorePassword) - sslContextFactory.setExcludeProtocols("SSL.*", "TLSv1", "TLSv1.1") - sslContextFactory.setIncludeProtocols("TLSv1.2") - sslContextFactory.setExcludeCipherSuites(".*NULL.*", ".*RC4.*", ".*MD5.*", ".*DES.*", ".*DSS.*") - sslContextFactory.setIncludeCipherSuites(".*AES.*GCM.*") - val sslConnector = ServerConnector(server, SslConnectionFactory(sslContextFactory, "http/1.1"), HttpConnectionFactory(httpsConfiguration)) - sslConnector.port = address.port - sslConnector - } else { - val httpConfiguration = HttpConfiguration() - httpConfiguration.outputBufferSize = 32768 - val httpConnector = ServerConnector(server, HttpConnectionFactory(httpConfiguration)) - httpConnector.port = address.port - httpConnector - } - server.connectors = arrayOf(connector) - - server.handler = handlerCollection + try { + val server = NodeWebServer(conf) server.start() - log.info("Started webserver on address $address") - return server + 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) } - - private fun buildServletContextHandler(localRpc: CordaRPCOps): ServletContextHandler { - return ServletContextHandler().apply { - contextPath = "/" - setAttribute("rpc", localRpc) - addServlet(DataUploadServlet::class.java, "/upload/*") - addServlet(AttachmentDownloadServlet::class.java, "/attachments/*") - - val resourceConfig = ResourceConfig() - resourceConfig.register(ObjectMapperConfig(localRpc)) - resourceConfig.register(ResponseFilter()) - resourceConfig.register(APIServerImpl(localRpc)) - - val webAPIsOnClasspath = pluginRegistries.flatMap { x -> x.webApis } - for (webapi in webAPIsOnClasspath) { - log.info("Add plugin web API from attachment $webapi") - val customAPI = try { - webapi.apply(localRpc) - } catch (ex: InvocationTargetException) { - log.error("Constructor $webapi threw an error: ", ex.targetException) - continue - } - resourceConfig.register(customAPI) - } - - val staticDirMaps = pluginRegistries.map { x -> x.staticServeDirs } - val staticDirs = staticDirMaps.flatMap { it.keys }.zip(staticDirMaps.flatMap { it.values }) - staticDirs.forEach { - val staticDir = ServletHolder(DefaultServlet::class.java) - staticDir.setInitParameter("resourceBase", it.second) - staticDir.setInitParameter("dirAllowed", "true") - staticDir.setInitParameter("pathInfoOnly", "true") - addServlet(staticDir, "/web/${it.first}/*") - } - - // Give the app a slightly better name in JMX rather than a randomly generated one and enable JMX - resourceConfig.addProperties(mapOf(ServerProperties.APPLICATION_NAME to "node.api", - ServerProperties.MONITORING_STATISTICS_MBEANS_ENABLED to "true")) - - val container = ServletContainer(resourceConfig) - val jerseyServlet = ServletHolder(container) - addServlet(jerseyServlet, "/api/*") - jerseyServlet.initOrder = 0 // Initialise at server start - } - } - - private fun retryConnectLocalRpc(): CordaRPCOps { - while (true) { - try { - return connectLocalRpcAsNodeUser() - } 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 - } catch (e: java.nio.file.NoSuchFileException) { - log.debug("Tried to open a file that doesn't yet exist, retrying", e) - Thread.sleep(retryDelay) - } - } - } - - private fun connectLocalRpcAsNodeUser(): CordaRPCOps { - log.info("Connecting to node at ${config.artemisAddress} as node user") - val client = CordaRPCClient(config.artemisAddress, config) - client.start(ArtemisMessagingComponent.NODE_USER, ArtemisMessagingComponent.NODE_USER) - return client.proxy() - } - - /** Fetch CordaPluginRegistry classes registered in META-INF/services/net.corda.core.node.CordaPluginRegistry files that exist in the classpath */ - val pluginRegistries: List by lazy { - ServiceLoader.load(CordaPluginRegistry::class.java).toList() - } -} \ No newline at end of file +} diff --git a/node/webserver/src/main/kotlin/net/corda/webserver/api/APIServer.kt b/node/webserver/src/main/kotlin/net/corda/webserver/api/APIServer.kt index 0447bbb601..e275dd7c49 100644 --- a/node/webserver/src/main/kotlin/net/corda/webserver/api/APIServer.kt +++ b/node/webserver/src/main/kotlin/net/corda/webserver/api/APIServer.kt @@ -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 diff --git a/node/webserver/src/main/kotlin/net/corda/webserver/api/Query.kt b/node/webserver/src/main/kotlin/net/corda/webserver/api/Query.kt index 9d5e34fe1b..1a8038df85 100644 --- a/node/webserver/src/main/kotlin/net/corda/webserver/api/Query.kt +++ b/node/webserver/src/main/kotlin/net/corda/webserver/api/Query.kt @@ -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. diff --git a/node/webserver/src/main/kotlin/net/corda/webserver/internal/APIServerImpl.kt b/node/webserver/src/main/kotlin/net/corda/webserver/internal/APIServerImpl.kt index 02155c51cc..49a9564f41 100644 --- a/node/webserver/src/main/kotlin/net/corda/webserver/internal/APIServerImpl.kt +++ b/node/webserver/src/main/kotlin/net/corda/webserver/internal/APIServerImpl.kt @@ -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 diff --git a/node/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt b/node/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt new file mode 100644 index 0000000000..d5eb5d113e --- /dev/null +++ b/node/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt @@ -0,0 +1,176 @@ +package net.corda.webserver.internal + +import net.corda.core.messaging.CordaRPCOps +import net.corda.core.node.CordaPluginRegistry +import net.corda.core.utilities.loggerFor +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.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 +import org.eclipse.jetty.servlet.DefaultServlet +import org.eclipse.jetty.servlet.ServletContextHandler +import org.eclipse.jetty.servlet.ServletHolder +import org.eclipse.jetty.util.ssl.SslContextFactory +import org.eclipse.jetty.webapp.WebAppContext +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.* + +class NodeWebServer(val config: FullNodeConfiguration) { + private companion object { + val log = loggerFor() + val retryDelay = 1000L // Milliseconds + } + + val address = config.webAddress + private lateinit var server: Server + + fun start() { + printBasicNodeInfo("Starting as webserver: ${config.webAddress}") + server = initWebServer(retryConnectLocalRpc()) + } + + fun run() { + while (server.isRunning) { + Thread.sleep(100) // TODO: Redesign + } + } + + private fun initWebServer(localRpc: CordaRPCOps): Server { + // Note that the web server handlers will all run concurrently, and not on the node thread. + val handlerCollection = HandlerCollection() + + // 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")) + val warpath = classpath.firstOrNull { it.contains("jolokia-agent-war-2") && it.endsWith(".war") } + if (warpath != null) { + handlerCollection.addHandler(WebAppContext().apply { + // Find the jolokia WAR file on the classpath. + contextPath = "/monitoring/json" + setInitParameter("mimeType", "application/json") + war = warpath + }) + } else { + log.warn("Unable to locate Jolokia WAR on classpath") + } + } + + // API, data upload and download to services (attachments, rates oracles etc) + handlerCollection.addHandler(buildServletContextHandler(localRpc)) + + val server = Server() + + val connector = if (config.useHTTPS) { + val httpsConfiguration = HttpConfiguration() + httpsConfiguration.outputBufferSize = 32768 + httpsConfiguration.addCustomizer(SecureRequestCustomizer()) + val sslContextFactory = SslContextFactory() + sslContextFactory.keyStorePath = config.keyStoreFile.toString() + sslContextFactory.setKeyStorePassword(config.keyStorePassword) + sslContextFactory.setKeyManagerPassword(config.keyStorePassword) + sslContextFactory.setTrustStorePath(config.trustStoreFile.toString()) + sslContextFactory.setTrustStorePassword(config.trustStorePassword) + sslContextFactory.setExcludeProtocols("SSL.*", "TLSv1", "TLSv1.1") + sslContextFactory.setIncludeProtocols("TLSv1.2") + sslContextFactory.setExcludeCipherSuites(".*NULL.*", ".*RC4.*", ".*MD5.*", ".*DES.*", ".*DSS.*") + sslContextFactory.setIncludeCipherSuites(".*AES.*GCM.*") + val sslConnector = ServerConnector(server, SslConnectionFactory(sslContextFactory, "http/1.1"), HttpConnectionFactory(httpsConfiguration)) + sslConnector.port = address.port + sslConnector + } else { + val httpConfiguration = HttpConfiguration() + httpConfiguration.outputBufferSize = 32768 + val httpConnector = ServerConnector(server, HttpConnectionFactory(httpConfiguration)) + log.info("Starting webserver on address $address") + httpConnector.port = address.port + httpConnector + } + server.connectors = arrayOf(connector) + + server.handler = handlerCollection + //runOnStop += Runnable { server.stop() } + server.start() + log.info("Server started") + log.info("Embedded web server is listening on", "http://${InetAddress.getLocalHost().hostAddress}:${connector.port}/") + return server + } + + private fun buildServletContextHandler(localRpc: CordaRPCOps): ServletContextHandler { + return ServletContextHandler().apply { + contextPath = "/" + setAttribute("rpc", localRpc) + addServlet(DataUploadServlet::class.java, "/upload/*") + addServlet(AttachmentDownloadServlet::class.java, "/attachments/*") + + val resourceConfig = ResourceConfig() + resourceConfig.register(ObjectMapperConfig(localRpc)) + resourceConfig.register(ResponseFilter()) + resourceConfig.register(APIServerImpl(localRpc)) + + val webAPIsOnClasspath = pluginRegistries.flatMap { x -> x.webApis } + for (webapi in webAPIsOnClasspath) { + log.info("Add plugin web API from attachment $webapi") + val customAPI = try { + webapi.apply(localRpc) + } catch (ex: InvocationTargetException) { + log.error("Constructor $webapi threw an error: ", ex.targetException) + continue + } + resourceConfig.register(customAPI) + } + + val staticDirMaps = pluginRegistries.map { x -> x.staticServeDirs } + val staticDirs = staticDirMaps.flatMap { it.keys }.zip(staticDirMaps.flatMap { it.values }) + staticDirs.forEach { + val staticDir = ServletHolder(DefaultServlet::class.java) + staticDir.setInitParameter("resourceBase", it.second) + staticDir.setInitParameter("dirAllowed", "true") + staticDir.setInitParameter("pathInfoOnly", "true") + addServlet(staticDir, "/web/${it.first}/*") + } + + // Give the app a slightly better name in JMX rather than a randomly generated one and enable JMX + resourceConfig.addProperties(mapOf(ServerProperties.APPLICATION_NAME to "node.api", + ServerProperties.MONITORING_STATISTICS_MBEANS_ENABLED to "true")) + + val container = ServletContainer(resourceConfig) + val jerseyServlet = ServletHolder(container) + addServlet(jerseyServlet, "/api/*") + jerseyServlet.initOrder = 0 // Initialise at server start + } + } + + private fun retryConnectLocalRpc(): CordaRPCOps { + while (true) { + try { + return connectLocalRpcAsNodeUser() + } catch (e: ActiveMQNotConnectedException) { + log.debug("Could not connect to ${config.artemisAddress} due to exception: ", e) + Thread.sleep(retryDelay) + } + } + } + + private fun connectLocalRpcAsNodeUser(): CordaRPCOps { + log.info("Connecting to node at ${config.artemisAddress} as node user") + val client = CordaRPCClient(config.artemisAddress, config) + client.start(ArtemisMessagingComponent.NODE_USER, ArtemisMessagingComponent.NODE_USER) + return client.proxy() + } + + /** Fetch CordaPluginRegistry classes registered in META-INF/services/net.corda.core.node.CordaPluginRegistry files that exist in the classpath */ + val pluginRegistries: List by lazy { + ServiceLoader.load(CordaPluginRegistry::class.java).toList() + } +} \ No newline at end of file diff --git a/node/webserver/src/main/kotlin/net/corda/webserver/servlets/AttachmentDownloadServlet.kt b/node/webserver/src/main/kotlin/net/corda/webserver/servlets/AttachmentDownloadServlet.kt index de40ec9bb4..fb113859d6 100644 --- a/node/webserver/src/main/kotlin/net/corda/webserver/servlets/AttachmentDownloadServlet.kt +++ b/node/webserver/src/main/kotlin/net/corda/webserver/servlets/AttachmentDownloadServlet.kt @@ -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 diff --git a/node/webserver/src/main/kotlin/net/corda/webserver/servlets/DataUploadServlet.kt b/node/webserver/src/main/kotlin/net/corda/webserver/servlets/DataUploadServlet.kt index 76a337128c..eb1efb7268 100644 --- a/node/webserver/src/main/kotlin/net/corda/webserver/servlets/DataUploadServlet.kt +++ b/node/webserver/src/main/kotlin/net/corda/webserver/servlets/DataUploadServlet.kt @@ -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() @@ -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) { diff --git a/node/webserver/src/main/kotlin/net/corda/webserver/servlets/ObjectMapperConfig.kt b/node/webserver/src/main/kotlin/net/corda/webserver/servlets/ObjectMapperConfig.kt index 78dbd1b131..b0304db015 100644 --- a/node/webserver/src/main/kotlin/net/corda/webserver/servlets/ObjectMapperConfig.kt +++ b/node/webserver/src/main/kotlin/net/corda/webserver/servlets/ObjectMapperConfig.kt @@ -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 diff --git a/node/webserver/src/main/kotlin/net/corda/webserver/servlets/ResponseFilter.kt b/node/webserver/src/main/kotlin/net/corda/webserver/servlets/ResponseFilter.kt index 6700b8bb0a..ec49edd182 100644 --- a/node/webserver/src/main/kotlin/net/corda/webserver/servlets/ResponseFilter.kt +++ b/node/webserver/src/main/kotlin/net/corda/webserver/servlets/ResponseFilter.kt @@ -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 diff --git a/node/src/main/kotlin/net/corda/node/utilities/JsonSupport.kt b/node/webserver/src/main/kotlin/net/corda/webserver/utilities/JsonSupport.kt similarity index 99% rename from node/src/main/kotlin/net/corda/node/utilities/JsonSupport.kt rename to node/webserver/src/main/kotlin/net/corda/webserver/utilities/JsonSupport.kt index 7b8c8380f2..97ca082708 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/JsonSupport.kt +++ b/node/webserver/src/main/kotlin/net/corda/webserver/utilities/JsonSupport.kt @@ -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 diff --git a/node/src/test/kotlin/net/corda/node/JsonSupportTest.kt b/node/webserver/src/test/kotlin/net/corda/webserver/JsonSupportTest.kt similarity index 86% rename from node/src/test/kotlin/net/corda/node/JsonSupportTest.kt rename to node/webserver/src/test/kotlin/net/corda/webserver/JsonSupportTest.kt index 7248f4fb7e..634d10957f 100644 --- a/node/src/test/kotlin/net/corda/node/JsonSupportTest.kt +++ b/node/webserver/src/test/kotlin/net/corda/webserver/JsonSupportTest.kt @@ -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 diff --git a/publish.properties b/publish.properties index 4ff037d9de..ef659e45f6 100644 --- a/publish.properties +++ b/publish.properties @@ -1 +1 @@ -gradlePluginsVersion=0.8.2 +gradlePluginsVersion=0.8.3 diff --git a/samples/attachment-demo/build.gradle b/samples/attachment-demo/build.gradle index e4158e7bc0..bcc0480780 100644 --- a/samples/attachment-demo/build.gradle +++ b/samples/attachment-demo/build.gradle @@ -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') diff --git a/samples/bank-of-corda-demo/build.gradle b/samples/bank-of-corda-demo/build.gradle index 806ba0417f..ecdcb39273 100644 --- a/samples/bank-of-corda-demo/build.gradle +++ b/samples/bank-of-corda-demo/build.gradle @@ -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') diff --git a/samples/irs-demo/build.gradle b/samples/irs-demo/build.gradle index 8c09fee88b..2a3ef1b15e 100644 --- a/samples/irs-demo/build.gradle +++ b/samples/irs-demo/build.gradle @@ -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 diff --git a/samples/irs-demo/src/main/kotlin/net/corda/simulation/IRSSimulation.kt b/samples/irs-demo/src/main/kotlin/net/corda/simulation/IRSSimulation.kt index fa2fd9e2d1..a25207ecd8 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/simulation/IRSSimulation.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/simulation/IRSSimulation.kt @@ -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() diff --git a/samples/network-visualiser/build.gradle b/samples/network-visualiser/build.gradle index e259cd0c54..bfb7fc9efa 100644 --- a/samples/network-visualiser/build.gradle +++ b/samples/network-visualiser/build.gradle @@ -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') diff --git a/samples/raft-notary-demo/build.gradle b/samples/raft-notary-demo/build.gradle index 2d775301d7..c6f17db1a2 100644 --- a/samples/raft-notary-demo/build.gradle +++ b/samples/raft-notary-demo/build.gradle @@ -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') diff --git a/samples/simm-valuation-demo/build.gradle b/samples/simm-valuation-demo/build.gradle index 5b8270c1c1..5bd12f3c7a 100644 --- a/samples/simm-valuation-demo/build.gradle +++ b/samples/simm-valuation-demo/build.gradle @@ -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') diff --git a/samples/trader-demo/build.gradle b/samples/trader-demo/build.gradle index 4619666340..2d5422d4f5 100644 --- a/samples/trader-demo/build.gradle +++ b/samples/trader-demo/build.gradle @@ -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') diff --git a/test-utils/build.gradle b/test-utils/build.gradle index 0f2ffbe060..ff3283245d 100644 --- a/test-utils/build.gradle +++ b/test-utils/build.gradle @@ -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}" diff --git a/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt b/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt index b1545ab93c..24ea03f3dd 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt @@ -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