diff --git a/build.gradle b/build.gradle index 00bcf3a4a1..f5d7e941f1 100644 --- a/build.gradle +++ b/build.gradle @@ -38,6 +38,9 @@ buildscript { ext.requery_version = '1.2.1' ext.dokka_version = '0.9.13' + // Update 121 is required for ObjectInputFilter and at time of writing 131 was latest: + ext.java8_minUpdateVersion = '131' + repositories { mavenLocal() mavenCentral() @@ -129,7 +132,7 @@ allprojects { // We recommend a specific minor version (unfortunately, not checkable directly) because JavaFX adds APIs in // minor releases, so we can't work with just any Java 8, it has to be a recent one. if (!JavaVersion.current().java8Compatible) - throw new GradleException("Corda requires Java 8, please upgrade to at least 1.8.0_112") + throw new GradleException("Corda requires Java 8, please upgrade to at least 1.8.0_$java8_minUpdateVersion") repositories { mavenCentral() diff --git a/core/src/main/kotlin/net/corda/core/serialization/DefaultKryoCustomizer.kt b/core/src/main/kotlin/net/corda/core/serialization/DefaultKryoCustomizer.kt index 6206ace573..4b9735c2b0 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/DefaultKryoCustomizer.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/DefaultKryoCustomizer.kt @@ -5,6 +5,7 @@ import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer import com.esotericsoftware.kryo.serializers.FieldSerializer import com.esotericsoftware.kryo.util.MapReferenceResolver import de.javakaffee.kryoserializers.ArraysAsListSerializer +import de.javakaffee.kryoserializers.BitSetSerializer import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer import de.javakaffee.kryoserializers.guava.* import net.corda.core.crypto.CompositeKey @@ -80,7 +81,7 @@ object DefaultKryoCustomizer { addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer()) register(MetaData::class.java, MetaDataSerializer) - register(BitSet::class.java, ReferencesAwareJavaSerializer) + register(BitSet::class.java, BitSetSerializer()) addDefaultSerializer(Logger::class.java, LoggerSerializer) diff --git a/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt b/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt index 5abd4e4a54..470a88aa5e 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt @@ -509,29 +509,6 @@ fun Kryo.withoutReferences(block: () -> T): T { } } -/** - * Improvement to the builtin JavaSerializer by honouring the [Kryo.getReferences] setting. - */ -object ReferencesAwareJavaSerializer : JavaSerializer() { - override fun write(kryo: Kryo, output: Output, obj: Any) { - if (kryo.references) { - super.write(kryo, output, obj) - } else { - ObjectOutputStream(output).use { - it.writeObject(obj) - } - } - } - - override fun read(kryo: Kryo, input: Input, type: Class): Any { - return if (kryo.references) { - super.read(kryo, input, type) - } else { - ObjectInputStream(input).use(ObjectInputStream::readObject) - } - } -} - val ATTACHMENT_STORAGE = "ATTACHMENT_STORAGE" val Kryo.attachmentStorage: AttachmentStorage? diff --git a/core/src/main/kotlin/net/corda/core/utilities/Logging.kt b/core/src/main/kotlin/net/corda/core/utilities/Logging.kt index c34039a4c6..b2c2c41dc3 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/Logging.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/Logging.kt @@ -3,6 +3,7 @@ package net.corda.core.utilities import org.apache.logging.log4j.Level import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.core.LoggerContext +import org.apache.logging.log4j.core.config.Configurator import org.apache.logging.log4j.core.config.LoggerConfig import org.slf4j.LoggerFactory import kotlin.reflect.KClass @@ -60,4 +61,19 @@ object LogHelper { config.addLogger(name, loggerConfig) loggerContext.updateLoggers(config) } + + /** + * May fail to restore the original level due to unavoidable race if called by multiple threads. + */ + inline fun withLevel(logName: String, levelName: String, block: () -> T) = let { + val level = Level.valueOf(levelName) + val oldLevel = LogManager.getLogger(logName).level + Configurator.setLevel(logName, level) + try { + block() + } finally { + Configurator.setLevel(logName, oldLevel) + } + } + } diff --git a/node/build.gradle b/node/build.gradle index 50ee64f020..2e65abcb69 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -34,6 +34,9 @@ sourceSets { runtimeClasspath += main.output + test.output srcDir file('src/integration-test/kotlin') } + resources { + srcDir file('src/integration-test/resources') + } } test { resources { @@ -61,6 +64,7 @@ dependencies { // Log4J: logging framework (with SLF4J bindings) compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}" compile "org.apache.logging.log4j:log4j-web:${log4j_version}" + compile "org.slf4j:jul-to-slf4j:$slf4j_version" compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" diff --git a/node/capsule/build.gradle b/node/capsule/build.gradle index 4c4aebda38..62ea52efc8 100644 --- a/node/capsule/build.gradle +++ b/node/capsule/build.gradle @@ -48,8 +48,7 @@ task buildCordaJAR(type: FatCapsule) { javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"] systemProperties['visualvm.display.name'] = 'Corda' minJavaVersion = '1.8.0' - // This version is known to work and avoids earlier 8u versions that have bugs. - minUpdateVersion['1.8'] = '102' + minUpdateVersion['1.8'] = java8_minUpdateVersion caplets = ['CordaCaplet'] // JVM configuration: diff --git a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt new file mode 100644 index 0000000000..bdc410e5db --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -0,0 +1,43 @@ +package net.corda.node + +import net.corda.core.flows.FlowLogic +import net.corda.core.getOrThrow +import net.corda.core.messaging.startFlow +import net.corda.core.node.CordaPluginRegistry +import net.corda.node.driver.driver +import net.corda.node.services.startFlowPermission +import net.corda.nodeapi.User +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.Test +import java.io.* + +class BootTests { + + @Test + fun `java deserialization is disabled`() { + driver { + val user = User("u", "p", setOf(startFlowPermission())) + val future = startNode(rpcUsers = listOf(user)).getOrThrow().rpcClientToNode().apply { + start(user.username, user.password) + }.proxy().startFlow(::ObjectInputStreamFlow).returnValue + assertThatThrownBy { future.getOrThrow() }.isInstanceOf(InvalidClassException::class.java).hasMessage("filter status: REJECTED") + } + } + +} + +class ObjectInputStreamFlow : FlowLogic() { + + override fun call() { + System.clearProperty("jdk.serialFilter") // This checks that the node has already consumed the property. + val data = ByteArrayOutputStream().apply { ObjectOutputStream(this).use { it.writeObject(object : Serializable {}) } }.toByteArray() + ObjectInputStream(data.inputStream()).use { it.readObject() } + } + +} + +class BootTestsPlugin : CordaPluginRegistry() { + + override val requiredFlows: Map> = mapOf(ObjectInputStreamFlow::class.java.name to setOf()) + +} 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 44fc106e2e..cc9dce7fc6 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 @@ -1,9 +1,9 @@ package net.corda.node.driver +import com.google.common.util.concurrent.ListenableFuture import net.corda.core.div import net.corda.core.getOrThrow import net.corda.core.list -import net.corda.core.node.NodeInfo import net.corda.core.node.services.ServiceInfo import net.corda.core.readLines import net.corda.core.utilities.DUMMY_BANK_A @@ -19,54 +19,51 @@ import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService class DriverTests { - companion object { - val executorService: ScheduledExecutorService = Executors.newScheduledThreadPool(2) - fun nodeMustBeUp(nodeInfo: NodeInfo) { + companion object { + + private val executorService: ScheduledExecutorService = Executors.newScheduledThreadPool(2) + + private fun nodeMustBeUp(handleFuture: ListenableFuture) = handleFuture.getOrThrow().apply { val hostAndPort = ArtemisMessagingComponent.toHostAndPort(nodeInfo.address) // Check that the port is bound - addressMustBeBound(executorService, hostAndPort) + addressMustBeBound(executorService, hostAndPort, process) } - fun nodeMustBeDown(nodeInfo: NodeInfo) { - val hostAndPort = ArtemisMessagingComponent.toHostAndPort(nodeInfo.address) + private fun nodeMustBeDown(handle: NodeHandle) { + val hostAndPort = ArtemisMessagingComponent.toHostAndPort(handle.nodeInfo.address) // Check that the port is bound addressMustNotBeBound(executorService, hostAndPort) } + } @Test fun `simple node startup and shutdown`() { - val (notary, regulator) = driver { + val handles = driver { val notary = startNode(DUMMY_NOTARY.name, setOf(ServiceInfo(SimpleNotaryService.type))) val regulator = startNode("Regulator", setOf(ServiceInfo(RegulatorService.type))) - - nodeMustBeUp(notary.getOrThrow().nodeInfo) - nodeMustBeUp(regulator.getOrThrow().nodeInfo) - Pair(notary.getOrThrow(), regulator.getOrThrow()) + listOf(nodeMustBeUp(notary), nodeMustBeUp(regulator)) } - nodeMustBeDown(notary.nodeInfo) - nodeMustBeDown(regulator.nodeInfo) + handles.map { nodeMustBeDown(it) } } @Test fun `starting node with no services`() { val noService = driver { val noService = startNode(DUMMY_BANK_A.name) - nodeMustBeUp(noService.getOrThrow().nodeInfo) - noService.getOrThrow() + nodeMustBeUp(noService) } - nodeMustBeDown(noService.nodeInfo) + nodeMustBeDown(noService) } @Test fun `random free port allocation`() { val nodeHandle = driver(portAllocation = PortAllocation.RandomFree) { val nodeInfo = startNode(DUMMY_BANK_A.name) - nodeMustBeUp(nodeInfo.getOrThrow().nodeInfo) - nodeInfo.getOrThrow() + nodeMustBeUp(nodeInfo) } - nodeMustBeDown(nodeHandle.nodeInfo) + nodeMustBeDown(nodeHandle) } @Test @@ -81,4 +78,5 @@ class DriverTests { assertThat(debugLinesPresent).isTrue() } } + } diff --git a/node/src/integration-test/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry b/node/src/integration-test/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry new file mode 100644 index 0000000000..4734e517b7 --- /dev/null +++ b/node/src/integration-test/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry @@ -0,0 +1 @@ +net.corda.node.BootTestsPlugin diff --git a/node/src/main/kotlin/net/corda/node/Corda.kt b/node/src/main/kotlin/net/corda/node/Corda.kt index fd5a624c7a..516037d46e 100644 --- a/node/src/main/kotlin/net/corda/node/Corda.kt +++ b/node/src/main/kotlin/net/corda/node/Corda.kt @@ -9,6 +9,7 @@ import net.corda.core.* import net.corda.core.node.NodeVersionInfo import net.corda.core.node.Version import net.corda.core.utilities.Emoji +import net.corda.core.utilities.LogHelper.withLevel import net.corda.node.internal.Node import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.shell.InteractiveShell @@ -17,10 +18,12 @@ import net.corda.node.utilities.registration.NetworkRegistrationHelper import org.fusesource.jansi.Ansi import org.fusesource.jansi.AnsiConsole import org.slf4j.LoggerFactory +import org.slf4j.bridge.SLF4JBridgeHandler +import java.io.* import java.lang.management.ManagementFactory import java.net.InetAddress -import java.nio.file.Path import java.nio.file.Paths +import java.util.Locale import kotlin.system.exitProcess private var renderBasicInfoToConsole = true @@ -34,9 +37,21 @@ fun printBasicNodeInfo(description: String, info: String? = null) { val LOGS_DIRECTORY_NAME = "logs" +private fun initLogging(cmdlineOptions: CmdLineOptions) { + val loggingLevel = cmdlineOptions.loggingLevel.name.toLowerCase(Locale.ENGLISH) + System.setProperty("defaultLogLevel", loggingLevel) // These properties are referenced from the XML config file. + if (cmdlineOptions.logToConsole) { + System.setProperty("consoleLogLevel", loggingLevel) + renderBasicInfoToConsole = false + } + System.setProperty("log-path", (cmdlineOptions.baseDirectory / LOGS_DIRECTORY_NAME).toString()) + SLF4JBridgeHandler.removeHandlersForRootLogger() // The default j.u.l config adds a ConsoleHandler. + SLF4JBridgeHandler.install() +} + fun main(args: Array) { val startTime = System.currentTimeMillis() - checkJavaVersion() + assertCanNormalizeEmptyPath() val argsParser = ArgsParser() @@ -48,13 +63,8 @@ fun main(args: Array) { exitProcess(1) } - // Set up logging. These properties are referenced from the XML config file. - val loggingLevel = cmdlineOptions.loggingLevel.name.toLowerCase() - System.setProperty("defaultLogLevel", loggingLevel) - if (cmdlineOptions.logToConsole) { - System.setProperty("consoleLogLevel", loggingLevel) - renderBasicInfoToConsole = false - } + initLogging(cmdlineOptions) + disableJavaDeserialization() // Should be after initLogging to avoid TMI. // Manifest properties are only available if running from the corda jar fun manifestValue(name: String): String? = if (Manifests.exists(name)) Manifests.read(name) else null @@ -79,9 +89,6 @@ fun main(args: Array) { drawBanner(nodeVersionInfo) - val dir: Path = cmdlineOptions.baseDirectory - System.setProperty("log-path", (dir / "logs").toString()) - val log = LoggerFactory.getLogger("Main") printBasicNodeInfo("Logs can be found in", System.getProperty("log-path")) @@ -137,7 +144,7 @@ fun main(args: Array) { val runShell = !cmdlineOptions.noLocalShell && System.console() != null node.startupComplete then { try { - InteractiveShell.startShell(dir, runShell, cmdlineOptions.sshdServer, node) + InteractiveShell.startShell(cmdlineOptions.baseDirectory, runShell, cmdlineOptions.sshdServer, node) } catch(e: Throwable) { log.error("Shell failed to start", e) } @@ -155,15 +162,34 @@ fun main(args: Array) { exitProcess(0) } -private fun checkJavaVersion() { +private fun assertCanNormalizeEmptyPath() { // Check we're not running a version of Java with a known bug: https://github.com/corda/corda/issues/83 try { Paths.get("").normalize() } catch (e: ArrayIndexOutOfBoundsException) { - println(""" + javaIsTooOld() + } +} + +private fun javaIsTooOld(): Nothing { + println(""" You are using a version of Java that is not supported (${System.getProperty("java.version")}). Please upgrade to the latest version. Corda will now exit...""") - exitProcess(1) + exitProcess(1) +} + +private fun disableJavaDeserialization() { + // ObjectInputFilter and friends are in java.io in Java 9 but sun.misc in backports, so we use the system property interface for portability: + System.setProperty("jdk.serialFilter", "maxbytes=0") + // Attempt a deserialization so that ObjectInputFilter (permanently) inits itself: + val data = ByteArrayOutputStream().apply { ObjectOutputStream(this).use { it.writeObject(object : Serializable {}) } }.toByteArray() + try { + withLevel("java.io.serialization", "WARN") { + ObjectInputStream(data.inputStream()).use { it.readObject() } // Logs REJECTED at INFO, which we don't want users to see. + } + javaIsTooOld() + } catch (e: InvalidClassException) { + // Good, our system property is honoured (assuming ObjectInputFilter wasn't inited earlier). } } 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 59cbff2a1b..1fd930a361 100644 --- a/node/src/main/kotlin/net/corda/node/driver/Driver.kt +++ b/node/src/main/kotlin/net/corda/node/driver/Driver.kt @@ -96,7 +96,7 @@ interface DriverDSLExposedInterface { * * @param handle The handle for the node that this webserver connects to via RPC. */ - fun startWebserver(handle: NodeHandle): ListenableFuture + fun startWebserver(handle: NodeHandle): ListenableFuture /** * Starts a network map service node. Note that only a single one should ever be running, so you will probably want @@ -122,6 +122,11 @@ data class NodeHandle( fun rpcClientToNode(): CordaRPCClient = CordaRPCClient(configuration.rpcAddress!!) } +data class WebserverHandle( + val listenAddress: HostAndPort, + val process: Process +) + sealed class PortAllocation { abstract fun nextPort(): Int fun nextHostAndPort(): HostAndPort = HostAndPort.fromParts("localhost", nextPort()) @@ -228,8 +233,16 @@ fun getTimestampAsDirectoryName(): String { return DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(UTC).format(Instant.now()) } -fun addressMustBeBound(executorService: ScheduledExecutorService, hostAndPort: HostAndPort): ListenableFuture { +class ListenProcessDeathException(message: String) : Exception(message) + +/** + * @throws ListenProcessDeathException if [listenProcess] dies before the check succeeds, i.e. the check can't succeed as intended. + */ +fun addressMustBeBound(executorService: ScheduledExecutorService, hostAndPort: HostAndPort, listenProcess: Process): ListenableFuture { return poll(executorService, "address $hostAndPort to bind") { + if (!listenProcess.isAlive) { + throw ListenProcessDeathException("The process that was expected to listen on $hostAndPort has died with status: ${listenProcess.exitValue()}") + } try { Socket(hostAndPort.host, hostAndPort.port).close() Unit @@ -265,12 +278,17 @@ fun poll( } var counter = 0 fun schedulePoll() { - executorService.schedule({ + executorService.schedule(task@ { counter++ if (counter == warnCount) { log.warn("Been polling $pollName for ${pollIntervalMs * warnCount / 1000.0} seconds...") } - val result = check() + val result = try { + check() + } catch (t: Throwable) { + resultFuture.setException(t) + return@task + } if (result == null) { schedulePoll() } else { @@ -482,7 +500,7 @@ class DriverDSL( } } - private fun queryWebserver(handle: NodeHandle, process: Process): HostAndPort { + private fun queryWebserver(handle: NodeHandle, process: Process): WebserverHandle { val protocol = if (handle.configuration.useHTTPS) "https://" else "http://" val url = URL("$protocol${handle.webAddress}/api/status") val client = OkHttpClient.Builder().connectTimeout(5, SECONDS).readTimeout(60, SECONDS).build() @@ -490,7 +508,7 @@ class DriverDSL( while (process.isAlive) try { val response = client.newCall(Request.Builder().url(url).build()).execute() if (response.isSuccessful && (response.body().string() == "started")) { - return handle.webAddress + return WebserverHandle(handle.webAddress, process) } } catch(e: ConnectException) { log.debug("Retrying webserver info at ${handle.webAddress}") @@ -499,13 +517,11 @@ class DriverDSL( throw IllegalStateException("Webserver at ${handle.webAddress} has died") } - override fun startWebserver(handle: NodeHandle): ListenableFuture { + override fun startWebserver(handle: NodeHandle): ListenableFuture { val debugPort = if (isDebug) debugPortAllocation.nextPort() else null - val process = DriverDSL.startWebserver(executorService, handle, debugPort) - registerProcess(process) - return process.map { - queryWebserver(handle, it) - } + val processFuture = DriverDSL.startWebserver(executorService, handle, debugPort) + registerProcess(processFuture) + return processFuture.map { queryWebserver(handle, it) } } override fun start() { @@ -577,7 +593,7 @@ class DriverDSL( errorLogPath = nodeConf.baseDirectory / LOGS_DIRECTORY_NAME / "error.log", workingDirectory = nodeConf.baseDirectory ) - }.flatMap { process -> addressMustBeBound(executorService, nodeConf.p2pAddress).map { process } } + }.flatMap { process -> addressMustBeBound(executorService, nodeConf.p2pAddress, process).map { process } } } private fun startWebserver( @@ -594,7 +610,7 @@ class DriverDSL( extraJvmArguments = listOf("-Dname=node-${handle.configuration.p2pAddress}-webserver"), errorLogPath = Paths.get("error.$className.log") ) - }.flatMap { process -> addressMustBeBound(executorService, handle.webAddress).map { process } } + }.flatMap { process -> addressMustBeBound(executorService, handle.webAddress, process).map { process } } } } } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt index 6067f20593..2b087966df 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt @@ -1,7 +1,10 @@ package net.corda.node.services.transactions import com.google.common.net.HostAndPort +import io.atomix.catalyst.buffer.BufferInput +import io.atomix.catalyst.buffer.BufferOutput import io.atomix.catalyst.serializer.Serializer +import io.atomix.catalyst.serializer.TypeSerializer import io.atomix.catalyst.transport.Address import io.atomix.catalyst.transport.Transport import io.atomix.catalyst.transport.netty.NettyTransport @@ -69,7 +72,21 @@ class RaftUniquenessProvider( val address = Address(myAddress.host, myAddress.port) val storage = buildStorage(storagePath) val transport = buildTransport(config) - val serializer = Serializer() + val serializer = Serializer().apply { + // Add serializers so Catalyst doesn't attempt to fall back on Java serialization for these types, which is disabled process-wide: + register(DistributedImmutableMap.Commands.PutAll::class.java) { + object : TypeSerializer> { + override fun write(obj: DistributedImmutableMap.Commands.PutAll<*, *>, buffer: BufferOutput>, serializer: Serializer) = writeMap(obj.entries, buffer, serializer) + override fun read(type: Class>, buffer: BufferInput>, serializer: Serializer) = DistributedImmutableMap.Commands.PutAll(readMap(buffer, serializer)) + } + } + register(LinkedHashMap::class.java) { + object : TypeSerializer> { + override fun write(obj: LinkedHashMap<*, *>, buffer: BufferOutput>, serializer: Serializer) = writeMap(obj, buffer, serializer) + override fun read(type: Class>, buffer: BufferInput>, serializer: Serializer) = readMap(buffer, serializer) + } + } + } server = CopycatServer.builder(address) .withStateMachine(stateMachineFactory) @@ -141,4 +158,16 @@ class RaftUniquenessProvider( fun String.toStateRef() = split(":").let { StateRef(SecureHash.parse(it[0]), it[1].toInt()) } return items.map { it.key.toStateRef() to it.value.deserialize() }.toMap() } -} \ No newline at end of file +} + +private fun writeMap(map: Map<*, *>, buffer: BufferOutput>, serializer: Serializer) = with(map) { + buffer.writeInt(size) + forEach { + with(serializer) { + writeObject(it.key, buffer) + writeObject(it.value, buffer) + } + } +} + +private fun readMap(buffer: BufferInput>, serializer: Serializer) = LinkedHashMap().apply { repeat(buffer.readInt()) { put(serializer.readObject(buffer), serializer.readObject(buffer)) } } diff --git a/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt b/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt index af8b96bab2..daf7d2c4b9 100644 --- a/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt +++ b/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt @@ -54,8 +54,6 @@ import java.util.* import java.util.concurrent.CountDownLatch import java.util.concurrent.ExecutionException import java.util.concurrent.Future -import java.util.logging.Level -import java.util.logging.Logger import kotlin.concurrent.thread // TODO: Add command history. @@ -80,8 +78,6 @@ object InteractiveShell { this.node = node var runSSH = runSSHServer - Logger.getLogger("").level = Level.OFF // TODO: Is this really needed? - val config = Properties() if (runSSH) { // TODO: Finish and enable SSH access. diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt index 105a4e965e..808761c166 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt @@ -18,7 +18,7 @@ class BankOfCordaHttpAPITest { startNode(BOC.name, setOf(ServiceInfo(SimpleNotaryService.type))), startNode("BigCorporation") ).getOrThrow() - val nodeBankOfCordaApiAddr = startWebserver(nodeBankOfCorda).getOrThrow() + val nodeBankOfCordaApiAddr = startWebserver(nodeBankOfCorda).getOrThrow().listenAddress assert(BankOfCordaClientApi(nodeBankOfCordaApiAddr).requestWebIssue(IssueRequestParams(1000, "USD", "BigCorporation", "1", BOC.name))) }, isDebug = true) } diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt index dfe1a8ed6c..0fd1fb2404 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt @@ -44,7 +44,7 @@ class IRSDemoTest : IntegrationTestCategory { startWebserver(controller), startWebserver(nodeA), startWebserver(nodeB) - ).getOrThrow() + ).getOrThrow().map { it.listenAddress } val nextFixingDates = getFixingDateObservable(nodeA.configuration) val numADeals = getTradeCount(nodeAAddr) diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index 932b496c29..34af1ecc04 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -36,7 +36,7 @@ class SimmValuationTest : IntegrationTestCategory { val (nodeA, nodeB) = Futures.allAsList(startNode(nodeALegalName), startNode(nodeBLegalName)).getOrThrow() val (nodeAApi, nodeBApi) = Futures.allAsList(startWebserver(nodeA), startWebserver(nodeB)) .getOrThrow() - .map { HttpApi.fromHostAndPort(it, "api/simmvaluationdemo") } + .map { HttpApi.fromHostAndPort(it.listenAddress, "api/simmvaluationdemo") } val nodeBParty = getPartyWithName(nodeAApi, nodeBLegalName) val nodeAParty = getPartyWithName(nodeBApi, nodeALegalName) diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/DriverBasedTest.kt b/test-utils/src/main/kotlin/net/corda/testing/node/DriverBasedTest.kt index 5421dfa7b3..c2fe6d2048 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/DriverBasedTest.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/DriverBasedTest.kt @@ -1,5 +1,7 @@ package net.corda.testing.node +import com.google.common.util.concurrent.SettableFuture +import net.corda.core.getOrThrow import net.corda.node.driver.DriverDSLExposedInterface import org.junit.After import org.junit.Before @@ -9,7 +11,7 @@ import kotlin.concurrent.thread abstract class DriverBasedTest { private val stopDriver = CountDownLatch(1) private var driverThread: Thread? = null - private lateinit var driverStarted: CountDownLatch + private lateinit var driverStarted: SettableFuture protected sealed class RunTestToken { internal object Token : RunTestToken() @@ -18,18 +20,22 @@ abstract class DriverBasedTest { protected abstract fun setup(): RunTestToken protected fun DriverDSLExposedInterface.runTest(): RunTestToken { - driverStarted.countDown() + driverStarted.set(Unit) stopDriver.await() return RunTestToken.Token } @Before fun start() { - driverStarted = CountDownLatch(1) + driverStarted = SettableFuture.create() driverThread = thread { - setup() + try { + setup() + } catch (t: Throwable) { + driverStarted.setException(t) + } } - driverStarted.await() + driverStarted.getOrThrow() } @After diff --git a/tools/explorer/capsule/build.gradle b/tools/explorer/capsule/build.gradle index 80f3964d38..a855e9af5a 100644 --- a/tools/explorer/capsule/build.gradle +++ b/tools/explorer/capsule/build.gradle @@ -39,8 +39,7 @@ task buildExplorerJAR(type: FatCapsule) { applicationVersion = corda_version systemProperties['visualvm.display.name'] = 'Node Explorer' minJavaVersion = '1.8.0' - // This version is known to work and avoids earlier 8u versions that have bugs. - minUpdateVersion['1.8'] = '102' + minUpdateVersion['1.8'] = java8_minUpdateVersion caplets = ['ExplorerCaplet'] // JVM configuration: diff --git a/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt b/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt index 16abc6cab2..7dad7ecae1 100644 --- a/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt +++ b/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt @@ -3,6 +3,7 @@ package net.corda.webserver import com.google.common.net.HostAndPort import net.corda.core.getOrThrow import net.corda.core.utilities.DUMMY_BANK_A +import net.corda.node.driver.WebserverHandle import net.corda.node.driver.addressMustBeBound import net.corda.node.driver.addressMustNotBeBound import net.corda.node.driver.driver @@ -13,8 +14,8 @@ class DriverTests { companion object { val executorService = Executors.newScheduledThreadPool(2) - fun webserverMustBeUp(webserverAddr: HostAndPort) { - addressMustBeBound(executorService, webserverAddr) + fun webserverMustBeUp(webserverHandle: WebserverHandle) { + addressMustBeBound(executorService, webserverHandle.listenAddress, webserverHandle.process) } fun webserverMustBeDown(webserverAddr: HostAndPort) { @@ -26,9 +27,9 @@ class DriverTests { fun `starting a node and independent web server works`() { val addr = driver { val node = startNode(DUMMY_BANK_A.name).getOrThrow() - val webserverAddr = startWebserver(node).getOrThrow() - webserverMustBeUp(webserverAddr) - webserverAddr + val webserverHandle = startWebserver(node).getOrThrow() + webserverMustBeUp(webserverHandle) + webserverHandle.listenAddress } webserverMustBeDown(addr) } diff --git a/webserver/webcapsule/build.gradle b/webserver/webcapsule/build.gradle index d036066d29..d046076a73 100644 --- a/webserver/webcapsule/build.gradle +++ b/webserver/webcapsule/build.gradle @@ -53,8 +53,7 @@ task buildWebserverJar(type: FatCapsule) { 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' + minUpdateVersion['1.8'] = java8_minUpdateVersion caplets = ['CordaCaplet'] // JVM configuration: @@ -81,4 +80,4 @@ publish { name = 'corda-webserver' publishWar = false // TODO: Use WAR instead of JAR disableDefaultJar = true -} \ No newline at end of file +}