diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index 90e4b8faca..1a6ce172cd 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -1,6 +1,5 @@ package net.corda.client.rpc; -import net.corda.client.rpc.internal.RPCClient; import net.corda.core.concurrent.CordaFuture; import net.corda.core.contracts.Amount; import net.corda.core.messaging.CordaRPCOps; @@ -42,7 +41,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest { private StartedNode node; private CordaRPCClient client; - private RPCClient.RPCConnection connection = null; + private RPCConnection connection = null; private CordaRPCOps rpcProxy; private void login(String username, String password) { diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt index e5ac356d76..635db3afea 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt @@ -10,31 +10,62 @@ import net.corda.nodeapi.ConnectionDirection import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT import java.time.Duration -/** @see RPCClient.RPCConnection */ -class CordaRPCConnection internal constructor( - connection: RPCClient.RPCConnection -) : RPCClient.RPCConnection by connection +/** + * This class is essentially just a wrapper for an RPCConnection and can be treated identically. + * + * @see RPCConnection + */ +class CordaRPCConnection internal constructor(connection: RPCConnection) : RPCConnection by connection -/** @see RPCClientConfiguration */ -data class CordaRPCClientConfiguration( - val connectionMaxRetryInterval: Duration -) { +/** + * Can be used to configure the RPC client connection. + * + * @property connectionMaxRetryInterval How much time to wait between connection retries if the server goes down. This + * time will be reached via exponential backoff. + */ +data class CordaRPCClientConfiguration(val connectionMaxRetryInterval: Duration) { internal fun toRpcClientConfiguration(): RPCClientConfiguration { return RPCClientConfiguration.default.copy( connectionMaxRetryInterval = connectionMaxRetryInterval ) } + companion object { + /** + * Returns the default configuration we recommend you use. + */ @JvmStatic - val default = CordaRPCClientConfiguration( - connectionMaxRetryInterval = RPCClientConfiguration.default.connectionMaxRetryInterval - ) + val default = CordaRPCClientConfiguration(connectionMaxRetryInterval = RPCClientConfiguration.default.connectionMaxRetryInterval) } } -/** @see RPCClient */ -//TODO Add SSL support -class CordaRPCClient( +/** + * An RPC client connects to the specified server and allows you to make calls to the server that perform various + * useful tasks. Please see the Client RPC section of docs.corda.net to learn more about how this API works. A brief + * description is provided here. + * + * Calling [start] returns an [RPCConnection] containing a proxy that lets you invoke RPCs on the server. Calls on + * it block, and if the server throws an exception then it will be rethrown on the client. Proxies are thread safe and + * may be used to invoke multiple RPCs in parallel. + * + * RPC sends and receives are logged on the net.corda.rpc logger. + * + * The [CordaRPCOps] defines what client RPCs are available. If an RPC returns an [rx.Observable] anywhere in the object + * graph returned then the server-side observable is transparently forwarded to the client side here. + * *You are expected to use it*. The server will begin sending messages immediately that will be buffered on the + * client, you are expected to drain by subscribing to the returned observer. You can opt-out of this by simply + * calling the [net.corda.client.rpc.notUsed] method on it. + * + * You don't have to explicitly close the observable if you actually subscribe to it: it will close itself and free up + * the server-side resources either when the client or JVM itself is shutdown, or when there are no more subscribers to + * it. Once all the subscribers to a returned observable are unsubscribed or the observable completes successfully or + * with an error, the observable is closed and you can't then re-subscribe again: you'll have to re-request a fresh + * observable with another RPC. + * + * @param hostAndPort The network address to connect to. + * @param configuration An optional configuration used to tweak client behaviour. + */ +class CordaRPCClient @JvmOverloads constructor( hostAndPort: NetworkHostAndPort, configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.default, initialiseSerialization: Boolean = true @@ -54,10 +85,24 @@ class CordaRPCClient( KRYO_RPC_CLIENT_CONTEXT ) + /** + * Logs in to the target server and returns an active connection. The returned connection is a [java.io.Closeable] + * and can be used with a try-with-resources statement. If you don't use that, you should use the + * [RPCConnection.notifyServerAndClose] or [RPCConnection.forceClose] methods to dispose of the connection object + * when done. + * + * @param username The username to authenticate with. + * @param password The password to authenticate with. + * @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout. + */ fun start(username: String, password: String): CordaRPCConnection { return CordaRPCConnection(rpcClient.start(CordaRPCOps::class.java, username, password)) } + /** + * A helper for Kotlin users that simply closes the connection after the block has executed. Be careful not to + * over-use this, as setting up and closing connections takes time. + */ inline fun use(username: String, password: String, block: (CordaRPCConnection) -> A): A { return start(username, password).use(block) } diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCConnection.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCConnection.kt new file mode 100644 index 0000000000..ae53adee53 --- /dev/null +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCConnection.kt @@ -0,0 +1,38 @@ +package net.corda.client.rpc + +import net.corda.core.messaging.RPCOps +import java.io.Closeable + +/** + * Holds a [proxy] object implementing [I] that forwards requests to the RPC server. The server version can be queried + * via this interface. + * + * [Closeable.close] may be used to shut down the connection and release associated resources. It is an + * alias for [notifyServerAndClose]. + */ +interface RPCConnection : Closeable { + /** + * Holds a synthetic class that automatically forwards method calls to the server, and returns the response. + */ + val proxy: I + + /** The RPC protocol version reported by the server. */ + val serverProtocolVersion: Int + + /** + * Closes this client gracefully by sending a notification to the server, so it can immediately clean up resources. + * If the server is not available this method may block for a short period until it's clear the server is not + * coming back. + */ + fun notifyServerAndClose() + + /** + * Closes this client without notifying the server. + * + * The server will eventually clear out the RPC message queue and disconnect subscribed observers, + * but this may take longer than desired, so to conserve resources you should normally use [notifyServerAndClose]. + * This method is helpful when the node may be shutting down or have already shut down and you don't want to + * block waiting for it to come back, which typically happens in integration tests and demos rather than production. + */ + fun forceClose() +} \ No newline at end of file diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt index f0fc941668..d4716f947f 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt @@ -1,5 +1,6 @@ package net.corda.client.rpc.internal +import net.corda.client.rpc.RPCConnection import net.corda.client.rpc.RPCException import net.corda.core.crypto.random63BitValue import net.corda.core.internal.logElapsedTime @@ -17,7 +18,6 @@ import net.corda.nodeapi.config.SSLConfiguration import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.TransportConfiguration import org.apache.activemq.artemis.api.core.client.ActiveMQClient -import java.io.Closeable import java.lang.reflect.Proxy import java.time.Duration @@ -79,12 +79,6 @@ data class RPCClientConfiguration( } } -/** - * An RPC client that may be used to create connections to an RPC server. - * - * @param transport The Artemis transport to use to connect to the server. - * @param rpcConfiguration Configuration used to tweak client behaviour. - */ class RPCClient( val transport: TransportConfiguration, val rpcConfiguration: RPCClientConfiguration = RPCClientConfiguration.default, @@ -101,54 +95,6 @@ class RPCClient( private val log = loggerFor>() } - /** - * Holds a proxy object implementing [I] that forwards requests to the RPC server. - * - * [Closeable.close] may be used to shut down the connection and release associated resources. - */ - interface RPCConnection : Closeable { - val proxy: I - /** The RPC protocol version reported by the server */ - val serverProtocolVersion: Int - - /** - * Closes this client without notifying the server. - * The server will eventually clear out the RPC message queue and disconnect subscribed observers, - * but this may take longer than desired, so to conserve resources you should normally use [notifyServerAndClose]. - * This method is helpful when the node may be shutting down or - * have already shut down and you don't want to block waiting for it to come back. - */ - fun forceClose() - - /** - * Closes this client gracefully by sending a notification to the server, so it can immediately clean up resources. - * If the server is not available this method may block for a short period until it's clear the server is not coming back. - */ - fun notifyServerAndClose() - } - - /** - * Returns an [RPCConnection] containing a proxy that lets you invoke RPCs on the server. Calls on it block, and if - * the server throws an exception then it will be rethrown on the client. Proxies are thread safe and may be used to - * invoke multiple RPCs in parallel. - * - * RPC sends and receives are logged on the net.corda.rpc logger. - * - * The [RPCOps] defines what client RPCs are available. If an RPC returns an [Observable] anywhere in the object - * graph returned then the server-side observable is transparently forwarded to the client side here. - * *You are expected to use it*. The server will begin sending messages immediately that will be buffered on the - * client, you are expected to drain by subscribing to the returned observer. You can opt-out of this by simply - * calling the [net.corda.client.rpc.notUsed] method on it. You don't have to explicitly close the observable if you actually - * subscribe to it: it will close itself and free up the server-side resources either when the client or JVM itself - * is shutdown, or when there are no more subscribers to it. Once all the subscribers to a returned observable are - * unsubscribed or the observable completes successfully or with an error, the observable is closed and you can't - * then re-subscribe again: you'll have to re-request a fresh observable with another RPC. - * - * @param rpcOpsClass The [Class] of the RPC interface. - * @param username The username to authenticate with. - * @param password The password to authenticate with. - * @throws RPCException if the server version is too low or if the server isn't reachable within the given time. - */ fun start( rpcOpsClass: Class, username: String,