diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt index 57122dc8c2..6779beb8c0 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt @@ -326,6 +326,46 @@ class RPCStabilityTests { } } + interface NoOps : RPCOps { + fun subscribe(): Observable + } + + @Test + fun `observables error when connection breaks`() { + rpcDriver { + val ops = object : NoOps { + override val protocolVersion = 0 + override fun subscribe(): Observable { + return PublishSubject.create() + } + } + val serverFollower = shutdownManager.follower() + val serverPort = startRpcServer(ops = ops).getOrThrow().broker.hostAndPort!! + serverFollower.unfollow() + + val clientConfiguration = RPCClientConfiguration.default.copy(connectionRetryInterval = 500.millis, maxReconnectAttempts = 1) + val clientFollower = shutdownManager.follower() + val client = startRpcClient(serverPort, configuration = clientConfiguration).getOrThrow() + clientFollower.unfollow() + + var terminateHandlerCalled = false + var errorHandlerCalled = false + val subscription = client.subscribe() + .doOnTerminate{ terminateHandlerCalled = true } + .doOnError { errorHandlerCalled = true } + .subscribe() + + serverFollower.shutdown() + Thread.sleep(100) + + assertTrue(terminateHandlerCalled) + assertTrue(errorHandlerCalled) + assertTrue(subscription.isUnsubscribed) + + clientFollower.shutdown() // Driver would do this after the new server, causing hang. + } + } + interface ThreadOps : RPCOps { fun sendMessage(id: Int, msgNo: Int): String } diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt index b2e0580952..b03ee81022 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt @@ -418,6 +418,14 @@ class RPCClientProxyHandler( when (event) { FailoverEventType.FAILURE_DETECTED -> { log.warn("RPC server unavailable. RPC calls are being buffered.") + log.warn("Terminating observables.") + val m = observableContext.observableMap.asMap() + m.keys.forEach { k -> + observationExecutorPool.run(k) { + m[k]?.onError(RPCException("Connection failure detected.")) + } + } + observableContext.observableMap.invalidateAll() } FailoverEventType.FAILOVER_COMPLETED -> { @@ -432,8 +440,6 @@ class RPCClientProxyHandler( "will throw an RPCException.") rpcReplyMap.forEach { id, replyFuture -> replyFuture.setException(RPCException("Could not re-connect to RPC server. Failover failed.")) - val observable = observableContext.observableMap.getIfPresent(id) - observable?.onError(RPCException("Could not re-connect to RPC server. Failover failed.")) } outgoingRequestBuffer.clear() rpcReplyMap.clear() @@ -517,4 +523,4 @@ object RpcClientObservableSerializer : Serializer>() { val rpcRequestOrObservableId = kryo.context[RPCApi.RpcRequestOrObservableIdKey] as InvocationId return observableContext.callSiteMap?.get(rpcRequestOrObservableId) } -} \ No newline at end of file +} diff --git a/core/build.gradle b/core/build.gradle index ea991614f7..430d83b6c8 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -118,8 +118,6 @@ dependencies { // This may be temporary until we experiment with other ways to do on-the-fly contract specialisation via an API. compile "org.apache.commons:commons-jexl3:3.0" compile 'commons-lang:commons-lang:2.6' - // For JSON - compile "com.fasterxml.jackson.core:jackson-databind:${jackson_version}" // Java ed25519 implementation. See https://github.com/str4d/ed25519-java/ compile "net.i2p.crypto:eddsa:$eddsa_version" diff --git a/node-api/src/main/kotlin/net/corda/core/serialization/ConstructorForDeserialization.kt b/core/src/main/kotlin/net/corda/core/serialization/ConstructorForDeserialization.kt similarity index 100% rename from node-api/src/main/kotlin/net/corda/core/serialization/ConstructorForDeserialization.kt rename to core/src/main/kotlin/net/corda/core/serialization/ConstructorForDeserialization.kt diff --git a/docs/source/api-flows.rst b/docs/source/api-flows.rst index 9dbb9a5d87..eebd1054e9 100644 --- a/docs/source/api-flows.rst +++ b/docs/source/api-flows.rst @@ -683,6 +683,12 @@ We then update the progress tracker's current step as we progress through the fl :end-before: DOCEND 18 :dedent: 12 +HTTP and database calls +----------------------- +HTTP, database and other calls to external resources are allowed in flows. However, their support is currently limited: + +* The call must be executed in a BLOCKING way. Flows don't currently support suspending to await the response to a call to an external resource +* The call must be idempotent. If the flow fails and has to restart from a checkpoint, the call will also be replayed Concurrency, Locking and Waiting -------------------------------- diff --git a/docs/source/key-concepts-transactions.rst b/docs/source/key-concepts-transactions.rst index 1e2c1ff836..b23219e4b9 100644 --- a/docs/source/key-concepts-transactions.rst +++ b/docs/source/key-concepts-transactions.rst @@ -105,7 +105,7 @@ will not be valid, and will not be accepted as inputs to subsequent transactions Other transaction components ---------------------------- -As well as input states and output states, transactions may contain: +As well as input states and output states, transactions contain: * Commands * Attachments @@ -178,4 +178,4 @@ In some cases, we want a transaction proposed to only be approved during a certa * A bond may only be redeemed before its expiry date In such cases, we can add a *time-window* to the transaction. Time-windows specify the time window during which the -transaction can be committed. We discuss time-windows in the section on :doc:`key-concepts-time-windows`. \ No newline at end of file +transaction can be committed. We discuss time-windows in the section on :doc:`key-concepts-time-windows`. diff --git a/docs/source/running-a-node.rst b/docs/source/running-a-node.rst index fb5fa63fa5..e13bfe6bfa 100644 --- a/docs/source/running-a-node.rst +++ b/docs/source/running-a-node.rst @@ -8,29 +8,15 @@ Running nodes locally There are several ways to run a Corda node locally for testing purposes. -Starting all nodes at once --------------------------- - -.. note:: ``runnodes`` is a shell script (or batch file on Windows) that is generated by ``deployNodes`` to allow you - to quickly start up all nodes and their webservers. ``runnodes`` should only be used for testing purposes. - -Start the nodes with ``runnodes`` by running the following command from the root of the project: - -* Linux/macOS: ``build/nodes/runnodes`` -* Windows: ``call build\nodes\runnodes.bat`` - -.. warning:: On macOS, do not click/change focus until all the node terminal windows have opened, or some processes may - fail to start. - -If you receive an ``OutOfMemoryError`` exception when interacting with the nodes, you need to increase the amount of -Java heap memory available to them, which you can do when running them individually. See -:ref:`starting-an-individual-corda-node`. +Starting a Corda node using DemoBench +------------------------------------- +See the instructions in :doc:`demobench`. .. _starting-an-individual-corda-node: -Starting an individual Corda node ---------------------------------- -Run the node by opening a terminal window in the node's folder and running: +Starting a Corda node from the command line +------------------------------------------- +Run a node by opening a terminal window in the node's folder and running: .. code-block:: shell @@ -57,8 +43,8 @@ Optionally run the node's webserver as well by opening a terminal window in the .. warning:: The node webserver is for testing purposes only and will be removed soon. -Starting a node with remote debugging enabled ---------------------------------------------- +Enabling remote debugging +~~~~~~~~~~~~~~~~~~~~~~~~~ To enable remote debugging of the node, run the following from the terminal window: ``java -Dcapsule.jvm.args="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" -jar corda.jar`` @@ -73,4 +59,18 @@ To enable export of JMX metrics over HTTP via `Jolokia `_, This command line will start the node with JMX metrics accessible via HTTP on port 7005. -See :ref:`Monitoring your node ` for further details. \ No newline at end of file +See :ref:`Monitoring your node ` for further details. + +Starting all nodes at once from the command line +------------------------------------------------ +If you created your nodes using ``deployNodes``, a ``runnodes`` shell script (or batch file on Windows) will have been +generated to allow you to quickly start up all nodes and their webservers. ``runnodes`` should only be used for testing +purposes. + +Start the nodes with ``runnodes`` by running the following command from the root of the project: + +* Linux/macOS: ``build/nodes/runnodes`` +* Windows: ``call build\nodes\runnodes.bat`` + +.. warning:: On macOS, do not click/change focus until all the node terminal windows have opened, or some processes may +fail to start. \ No newline at end of file diff --git a/finance/build.gradle b/finance/build.gradle index caa2bc6fe1..f7bef861b1 100644 --- a/finance/build.gradle +++ b/finance/build.gradle @@ -42,6 +42,9 @@ dependencies { // TODO Remove this once we have app configs compile "com.typesafe:config:$typesafe_config_version" + // For JSON + compile "com.fasterxml.jackson.core:jackson-databind:${jackson_version}" + testCompile project(':test-utils') testCompile project(path: ':core', configuration: 'testArtifacts') testCompile "junit:junit:$junit_version" @@ -72,6 +75,12 @@ artifacts { jar { baseName 'corda-finance' + exclude "META-INF/*.DSA" + exclude "META-INF/*.RSA" + exclude "META-INF/*.SF" + exclude "META-INF/*.MF" + exclude "META-INF/LICENSE" + exclude "META-INF/NOTICE" } publish { diff --git a/node-api/build.gradle b/node-api/build.gradle index 337dbc254b..2309959eb3 100644 --- a/node-api/build.gradle +++ b/node-api/build.gradle @@ -61,6 +61,8 @@ dependencies { // Pure-Java Snappy compression compile 'org.iq80.snappy:snappy:0.4' + compile "com.fasterxml.jackson.core:jackson-databind:$jackson_version" + // Unit testing helpers. testCompile "junit:junit:$junit_version" testCompile "org.assertj:assertj-core:$assertj_version"