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 6ceff2b9d0..6087429036 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 @@ -100,6 +100,8 @@ class RPCClientProxyHandler( private val log = contextLogger() // To check whether toString() is being invoked val toStringMethod: Method = Object::toString.javaMethod!! + val equalsMethod: Method = Object::equals.javaMethod!! + val hashCodeMethod: Method = Object::hashCode.javaMethod!! private fun addRpcCallSiteToThrowable(throwable: Throwable, callSite: CallSite) { var currentThrowable = throwable @@ -234,7 +236,13 @@ class RPCClientProxyHandler( lifeCycle.requireState { it == State.STARTED || it == State.SERVER_VERSION_NOT_SET } checkProtocolVersion(method) if (method == toStringMethod) { - return "Client RPC proxy for $rpcOpsClass" + return toString() + } + if (method == equalsMethod) { + return equals(arguments?.getOrNull(0)) + } + if (method == hashCodeMethod) { + return hashCode() } if (consumerSession!!.isClosed) { throw RPCException("RPC Proxy is closed") @@ -364,6 +372,8 @@ class RPCClientProxyHandler( close(false) } + + /** * Closes this handler and sends notifications to all observables, so it can immediately clean up resources. * Notifications sent to observables are to be acknowledged, therefore this call blocks until all acknowledgements are received. @@ -568,6 +578,32 @@ class RPCClientProxyHandler( rpcReplyMap.clear() callSiteMap?.clear() } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as RPCClientProxyHandler + + if (rpcUsername != other.rpcUsername) return false + if (clientAddress != other.clientAddress) return false + if (sessionId != other.sessionId) return false + if (targetLegalIdentity != other.targetLegalIdentity) return false + + return true + } + + override fun hashCode(): Int { + var result = rpcUsername.hashCode() + result = 31 * result + clientAddress.hashCode() + result = 31 * result + sessionId.hashCode() + result = 31 * result + (targetLegalIdentity?.hashCode() ?: 0) + return result + } + + override fun toString(): String { + return "{rpcUsername='$rpcUsername', clientAddress=$clientAddress, sessionId=$sessionId, targetLegalIdentity=$targetLegalIdentity}" + } } private typealias RpcObservableMap = Cache>> diff --git a/node/src/integration-test/kotlin/net/corda/node/services/rpc/NodeHandleTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/rpc/NodeHandleTests.kt new file mode 100644 index 0000000000..77852b5f7a --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/node/services/rpc/NodeHandleTests.kt @@ -0,0 +1,21 @@ +package net.corda.node.services.rpc + +import net.corda.core.utilities.getOrThrow +import net.corda.testing.driver.DriverParameters +import net.corda.testing.driver.driver +import org.assertj.core.api.Assertions.assertThatCode +import org.junit.Test + +class NodeHandleTests { + @Test + fun object_defined_functions_are_static_for_node_rpc_ops() { + driver(DriverParameters(startNodesInProcess = true)) { + val rpcClient = startNode().getOrThrow().rpc + + assertThatCode { rpcClient.hashCode() }.doesNotThrowAnyException() + @Suppress("UnusedEquals") + assertThatCode { rpcClient == rpcClient }.doesNotThrowAnyException() + assertThatCode { rpcClient.toString() }.doesNotThrowAnyException() + } + } +} \ No newline at end of file