ENT-3496 Move dumpCheckpoints to the new InternalCordaRPCOps interface

To prevent making `dumpCheckpoints` part of the public API a new
interface, `InternalCordaRPCOps` has been created and the function
has been moved there. `InternalCordaRPCOps` inherits from
`CordaRPCOps`.

`CordaRPCOpsImpl` now implements `InternalCordaRPCOps`.

`RunShellCommand` and `StringToMethodCallParser` required additional
changes due to issues handling inherited functions. This has only been
raised now due to `InternalCordaRPCOps` inheriting from `CordaRPCOps`.

Many classes have had references to `CordaRPCOps` changed to
`InternalCordaRPCOps`.
This commit is contained in:
LankyDan
2019-06-10 16:15:03 +01:00
parent d8395baf62
commit 31a0c077b8
17 changed files with 126 additions and 80 deletions

View File

@ -239,15 +239,17 @@ open class StringToMethodCallParser<in T : Any> @JvmOverloads constructor(
/** Returns a string-to-string map of commands to a string describing available parameter types. */ /** Returns a string-to-string map of commands to a string describing available parameter types. */
val availableCommands: Map<String, String> val availableCommands: Map<String, String>
get() { get() {
return methodMap.entries().map { entry -> return methodMap.entries().mapNotNull { entry ->
val (name, args) = entry // TODO: Kotlin 1.1 val (name, args) = entry // TODO: Kotlin 1.1
val argStr = if (args.parameterCount == 0) "" else { val argStr: String? = if (args.parameterCount == 0) "" else {
val paramNames = methodParamNames[name]!! val paramNames = methodParamNames[name]?. let { params ->
val typeNames = args.parameters.map { it.type.simpleName } val typeNames = args.parameters.map { it.type.simpleName }
val paramTypes = paramNames.zip(typeNames) val paramTypes = params.zip(typeNames)
paramTypes.joinToString(", ") { "${it.first}: ${it.second}" } paramTypes.joinToString(", ") { "${it.first}: ${it.second}" }
}
paramNames
} }
Pair(name, argStr) if(argStr == null) null else Pair(name, argStr)
}.toMap() }.toMap()
} }
} }

View File

@ -8,6 +8,7 @@ import net.corda.core.context.Actor
import net.corda.core.context.Trace import net.corda.core.context.Trace
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.PLATFORM_VERSION import net.corda.core.internal.PLATFORM_VERSION
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.messaging.ClientRpcSslOptions import net.corda.core.messaging.ClientRpcSslOptions
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.serialization.SerializationCustomSerializer import net.corda.core.serialization.SerializationCustomSerializer
@ -310,7 +311,7 @@ class CordaRPCClient private constructor(
} }
} }
private fun getRpcClient(): RPCClient<CordaRPCOps> { private fun getRpcClient(): RPCClient<InternalCordaRPCOps> {
return when { return when {
// Client->RPC broker // Client->RPC broker
haAddressPool.isEmpty() -> RPCClient( haAddressPool.isEmpty() -> RPCClient(
@ -385,7 +386,7 @@ class CordaRPCClient private constructor(
* @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout. * @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, externalTrace: Trace?, impersonatedActor: Actor?, targetLegalIdentity: CordaX500Name?): CordaRPCConnection { fun start(username: String, password: String, externalTrace: Trace?, impersonatedActor: Actor?, targetLegalIdentity: CordaX500Name?): CordaRPCConnection {
return CordaRPCConnection(getRpcClient().start(CordaRPCOps::class.java, username, password, externalTrace, impersonatedActor, targetLegalIdentity)) return CordaRPCConnection(getRpcClient().start(InternalCordaRPCOps::class.java, username, password, externalTrace, impersonatedActor, targetLegalIdentity))
} }
/** /**

View File

@ -3,6 +3,7 @@ package net.corda.client.rpc.internal
import net.corda.client.rpc.* import net.corda.client.rpc.*
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.internal.times import net.corda.core.internal.times
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import net.corda.core.messaging.ClientRpcSslOptions import net.corda.core.messaging.ClientRpcSslOptions
@ -44,7 +45,7 @@ class ReconnectingCordaRPCOps private constructor(
private val reconnectingRPCConnection: ReconnectingRPCConnection, private val reconnectingRPCConnection: ReconnectingRPCConnection,
private val observersPool: ExecutorService, private val observersPool: ExecutorService,
private val userPool: Boolean private val userPool: Boolean
) : AutoCloseable, CordaRPCOps by proxy(reconnectingRPCConnection, observersPool) { ) : AutoCloseable, InternalCordaRPCOps by proxy(reconnectingRPCConnection, observersPool) {
// Constructors that mirror CordaRPCClient. // Constructors that mirror CordaRPCClient.
constructor( constructor(
@ -77,11 +78,11 @@ class ReconnectingCordaRPCOps private constructor(
const val MAX_RETRY_ATTEMPTS_ON_AUTH_ERROR = 3 const val MAX_RETRY_ATTEMPTS_ON_AUTH_ERROR = 3
private val log = contextLogger() private val log = contextLogger()
private fun proxy(reconnectingRPCConnection: ReconnectingRPCConnection, observersPool: ExecutorService): CordaRPCOps { private fun proxy(reconnectingRPCConnection: ReconnectingRPCConnection, observersPool: ExecutorService): InternalCordaRPCOps {
return Proxy.newProxyInstance( return Proxy.newProxyInstance(
this::class.java.classLoader, this::class.java.classLoader,
arrayOf(CordaRPCOps::class.java), arrayOf(InternalCordaRPCOps::class.java),
ErrorInterceptingHandler(reconnectingRPCConnection, observersPool)) as CordaRPCOps ErrorInterceptingHandler(reconnectingRPCConnection, observersPool)) as InternalCordaRPCOps
} }
} }

View File

@ -109,9 +109,6 @@ interface CordaRPCOps : RPCOps {
@RPCReturnsObservables @RPCReturnsObservables
fun stateMachinesFeed(): DataFeed<List<StateMachineInfo>, StateMachineUpdate> fun stateMachinesFeed(): DataFeed<List<StateMachineInfo>, StateMachineUpdate>
/** Dump all the current flow checkpoints as JSON into a zip file in the node's directory. */
fun dumpCheckpoints()
/** /**
* Returns a snapshot of vault states for a given query criteria (and optional order and paging specification) * Returns a snapshot of vault states for a given query criteria (and optional order and paging specification)
* *

View File

@ -21,6 +21,7 @@ import net.corda.core.internal.*
import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.concurrent.doneFuture
import net.corda.core.internal.concurrent.map import net.corda.core.internal.concurrent.map
import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.internal.notary.NotaryService import net.corda.core.internal.notary.NotaryService
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.core.node.* import net.corda.core.node.*
@ -259,7 +260,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
/** The implementation of the [CordaRPCOps] interface used by this node. */ /** The implementation of the [CordaRPCOps] interface used by this node. */
open fun makeRPCOps(cordappLoader: CordappLoader, checkpointDumper: CheckpointDumper): CordaRPCOps { open fun makeRPCOps(cordappLoader: CordappLoader, checkpointDumper: CheckpointDumper): CordaRPCOps {
val ops: CordaRPCOps = CordaRPCOpsImpl( val ops: InternalCordaRPCOps = CordaRPCOpsImpl(
services, services,
smm, smm,
flowStarter, flowStarter,
@ -267,7 +268,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
) { ) {
shutdownExecutor.submit(::stop) shutdownExecutor.submit(::stop)
}.also { it.closeOnStop() } }.also { it.closeOnStop() }
val proxies = mutableListOf<(CordaRPCOps) -> CordaRPCOps>() val proxies = mutableListOf<(InternalCordaRPCOps) -> InternalCordaRPCOps>()
// Mind that order is relevant here. // Mind that order is relevant here.
proxies += ::AuthenticatedRpcOpsProxy proxies += ::AuthenticatedRpcOpsProxy
if (!configuration.devMode) { if (!configuration.devMode) {

View File

@ -17,6 +17,7 @@ import net.corda.core.identity.Party
import net.corda.core.internal.FlowStateMachine import net.corda.core.internal.FlowStateMachine
import net.corda.core.internal.RPC_UPLOADER import net.corda.core.internal.RPC_UPLOADER
import net.corda.core.internal.STRUCTURAL_STEP_PREFIX import net.corda.core.internal.STRUCTURAL_STEP_PREFIX
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.internal.sign import net.corda.core.internal.sign
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters
@ -54,7 +55,7 @@ internal class CordaRPCOpsImpl(
private val flowStarter: FlowStarter, private val flowStarter: FlowStarter,
private val checkpointDumper: CheckpointDumper, private val checkpointDumper: CheckpointDumper,
private val shutdownNode: () -> Unit private val shutdownNode: () -> Unit
) : CordaRPCOps, AutoCloseable { ) : InternalCordaRPCOps, AutoCloseable {
private companion object { private companion object {
private val logger = loggerFor<CordaRPCOpsImpl>() private val logger = loggerFor<CordaRPCOpsImpl>()

View File

@ -2,6 +2,7 @@ package net.corda.node.internal.rpc.proxies
import net.corda.client.rpc.PermissionException import net.corda.client.rpc.PermissionException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.node.internal.InvocationHandlerTemplate import net.corda.node.internal.InvocationHandlerTemplate
import net.corda.node.services.rpc.RpcAuthContext import net.corda.node.services.rpc.RpcAuthContext
@ -12,7 +13,7 @@ import java.lang.reflect.Proxy
/** /**
* Implementation of [CordaRPCOps] that checks authorisation. * Implementation of [CordaRPCOps] that checks authorisation.
*/ */
internal class AuthenticatedRpcOpsProxy(private val delegate: CordaRPCOps) : CordaRPCOps by proxy(delegate, ::rpcContext) { internal class AuthenticatedRpcOpsProxy(private val delegate: InternalCordaRPCOps) : InternalCordaRPCOps by proxy(delegate, ::rpcContext) {
/** /**
* Returns the RPC protocol version, which is the same the node's Platform Version. Exists since version 1 so guaranteed * Returns the RPC protocol version, which is the same the node's Platform Version. Exists since version 1 so guaranteed
* to be present. * to be present.
@ -32,13 +33,13 @@ internal class AuthenticatedRpcOpsProxy(private val delegate: CordaRPCOps) : Cor
} }
private companion object { private companion object {
private fun proxy(delegate: CordaRPCOps, context: () -> RpcAuthContext): CordaRPCOps { private fun proxy(delegate: InternalCordaRPCOps, context: () -> RpcAuthContext): InternalCordaRPCOps {
val handler = PermissionsEnforcingInvocationHandler(delegate, context) val handler = PermissionsEnforcingInvocationHandler(delegate, context)
return Proxy.newProxyInstance(delegate::class.java.classLoader, arrayOf(CordaRPCOps::class.java), handler) as CordaRPCOps return Proxy.newProxyInstance(delegate::class.java.classLoader, arrayOf(InternalCordaRPCOps::class.java), handler) as InternalCordaRPCOps
} }
} }
private class PermissionsEnforcingInvocationHandler(override val delegate: CordaRPCOps, private val context: () -> RpcAuthContext) : InvocationHandlerTemplate { private class PermissionsEnforcingInvocationHandler(override val delegate: InternalCordaRPCOps, private val context: () -> RpcAuthContext) : InvocationHandlerTemplate {
override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?) = guard(method.name, context) { super.invoke(proxy, method, arguments) } override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?) = guard(method.name, context) { super.invoke(proxy, method, arguments) }
} }
} }

View File

@ -1,22 +1,14 @@
package net.corda.node.internal.rpc.proxies package net.corda.node.internal.rpc.proxies
import net.corda.core.ClientRelevantError import net.corda.core.*
import net.corda.core.CordaException
import net.corda.core.CordaRuntimeException
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.contracts.TransactionVerificationException import net.corda.core.contracts.TransactionVerificationException
import net.corda.core.doOnError
import net.corda.core.flows.IdentifiableException import net.corda.core.flows.IdentifiableException
import net.corda.core.internal.concurrent.doOnError import net.corda.core.internal.concurrent.doOnError
import net.corda.core.internal.concurrent.mapError import net.corda.core.internal.concurrent.mapError
import net.corda.core.internal.declaredField import net.corda.core.internal.declaredField
import net.corda.core.mapErrors import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.*
import net.corda.core.messaging.DataFeed
import net.corda.core.messaging.FlowHandle
import net.corda.core.messaging.FlowHandleImpl
import net.corda.core.messaging.FlowProgressHandle
import net.corda.core.messaging.FlowProgressHandleImpl
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.internal.InvocationHandlerTemplate import net.corda.node.internal.InvocationHandlerTemplate
import net.corda.nodeapi.exceptions.InternalNodeException import net.corda.nodeapi.exceptions.InternalNodeException
@ -25,7 +17,7 @@ import java.lang.reflect.Method
import java.lang.reflect.Proxy.newProxyInstance import java.lang.reflect.Proxy.newProxyInstance
import kotlin.reflect.KClass import kotlin.reflect.KClass
internal class ExceptionMaskingRpcOpsProxy(private val delegate: CordaRPCOps, doLog: Boolean) : CordaRPCOps by proxy(delegate, doLog) { internal class ExceptionMaskingRpcOpsProxy(private val delegate: InternalCordaRPCOps, doLog: Boolean) : InternalCordaRPCOps by proxy(delegate, doLog) {
private companion object { private companion object {
private val logger = loggerFor<ExceptionMaskingRpcOpsProxy>() private val logger = loggerFor<ExceptionMaskingRpcOpsProxy>()
@ -34,13 +26,13 @@ internal class ExceptionMaskingRpcOpsProxy(private val delegate: CordaRPCOps, do
TransactionVerificationException::class TransactionVerificationException::class
) )
private fun proxy(delegate: CordaRPCOps, doLog: Boolean): CordaRPCOps { private fun proxy(delegate: InternalCordaRPCOps, doLog: Boolean): InternalCordaRPCOps {
val handler = ErrorObfuscatingInvocationHandler(delegate, whitelist, doLog) val handler = ErrorObfuscatingInvocationHandler(delegate, whitelist, doLog)
return newProxyInstance(delegate::class.java.classLoader, arrayOf(CordaRPCOps::class.java), handler) as CordaRPCOps return newProxyInstance(delegate::class.java.classLoader, arrayOf(InternalCordaRPCOps::class.java), handler) as InternalCordaRPCOps
} }
} }
private class ErrorObfuscatingInvocationHandler(override val delegate: CordaRPCOps, private val whitelist: Set<KClass<*>>, private val doLog: Boolean) : InvocationHandlerTemplate { private class ErrorObfuscatingInvocationHandler(override val delegate: InternalCordaRPCOps, private val whitelist: Set<KClass<*>>, private val doLog: Boolean) : InvocationHandlerTemplate {
override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?): Any? { override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?): Any? {
try { try {
val result = super.invoke(proxy, method, arguments) val result = super.invoke(proxy, method, arguments)

View File

@ -1,18 +1,13 @@
package net.corda.node.internal.rpc.proxies package net.corda.node.internal.rpc.proxies
import net.corda.core.CordaRuntimeException import net.corda.core.CordaRuntimeException
import net.corda.core.CordaThrowable
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.doOnError import net.corda.core.doOnError
import net.corda.core.internal.concurrent.doOnError import net.corda.core.internal.concurrent.doOnError
import net.corda.core.internal.concurrent.mapError import net.corda.core.internal.concurrent.mapError
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.mapErrors import net.corda.core.mapErrors
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.*
import net.corda.core.messaging.DataFeed
import net.corda.core.messaging.FlowHandle
import net.corda.core.messaging.FlowHandleImpl
import net.corda.core.messaging.FlowProgressHandle
import net.corda.core.messaging.FlowProgressHandleImpl
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.internal.InvocationHandlerTemplate import net.corda.node.internal.InvocationHandlerTemplate
@ -20,17 +15,17 @@ import rx.Observable
import java.lang.reflect.Method import java.lang.reflect.Method
import java.lang.reflect.Proxy.newProxyInstance import java.lang.reflect.Proxy.newProxyInstance
internal class ExceptionSerialisingRpcOpsProxy(private val delegate: CordaRPCOps, doLog: Boolean) : CordaRPCOps by proxy(delegate, doLog) { internal class ExceptionSerialisingRpcOpsProxy(private val delegate: InternalCordaRPCOps, doLog: Boolean) : InternalCordaRPCOps by proxy(delegate, doLog) {
private companion object { private companion object {
private val logger = loggerFor<ExceptionSerialisingRpcOpsProxy>() private val logger = loggerFor<ExceptionSerialisingRpcOpsProxy>()
private fun proxy(delegate: CordaRPCOps, doLog: Boolean): CordaRPCOps { private fun proxy(delegate: InternalCordaRPCOps, doLog: Boolean): InternalCordaRPCOps {
val handler = ErrorSerialisingInvocationHandler(delegate, doLog) val handler = ErrorSerialisingInvocationHandler(delegate, doLog)
return newProxyInstance(delegate::class.java.classLoader, arrayOf(CordaRPCOps::class.java), handler) as CordaRPCOps return newProxyInstance(delegate::class.java.classLoader, arrayOf(InternalCordaRPCOps::class.java), handler) as InternalCordaRPCOps
} }
} }
private class ErrorSerialisingInvocationHandler(override val delegate: CordaRPCOps, private val doLog: Boolean) : InvocationHandlerTemplate { private class ErrorSerialisingInvocationHandler(override val delegate: InternalCordaRPCOps, private val doLog: Boolean) : InvocationHandlerTemplate {
override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?): Any? { override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?): Any? {
try { try {
val result = super.invoke(proxy, method, arguments) val result = super.invoke(proxy, method, arguments)

View File

@ -1,6 +1,7 @@
package net.corda.node.internal.rpc.proxies package net.corda.node.internal.rpc.proxies
import net.corda.core.internal.executeWithThreadContextClassLoader import net.corda.core.internal.executeWithThreadContextClassLoader
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.node.internal.InvocationHandlerTemplate import net.corda.node.internal.InvocationHandlerTemplate
import java.lang.reflect.Method import java.lang.reflect.Method
@ -12,15 +13,15 @@ import java.lang.reflect.Proxy
* without sensible fallbacks to the classloader of the current instance. * without sensible fallbacks to the classloader of the current instance.
* If clients' CorDapps use one of these libraries, this temporary adjustment can ensure that any provided classes from these libraries will be available during RPC calls. * If clients' CorDapps use one of these libraries, this temporary adjustment can ensure that any provided classes from these libraries will be available during RPC calls.
*/ */
internal class ThreadContextAdjustingRpcOpsProxy(private val delegate: CordaRPCOps, private val classLoader: ClassLoader): CordaRPCOps by proxy(delegate, classLoader) { internal class ThreadContextAdjustingRpcOpsProxy(private val delegate: InternalCordaRPCOps, private val classLoader: ClassLoader): InternalCordaRPCOps by proxy(delegate, classLoader) {
private companion object { private companion object {
private fun proxy(delegate: CordaRPCOps, classLoader: ClassLoader): CordaRPCOps { private fun proxy(delegate: InternalCordaRPCOps, classLoader: ClassLoader): InternalCordaRPCOps {
val handler = ThreadContextAdjustingRpcOpsProxy.ThreadContextAdjustingInvocationHandler(delegate, classLoader) val handler = ThreadContextAdjustingRpcOpsProxy.ThreadContextAdjustingInvocationHandler(delegate, classLoader)
return Proxy.newProxyInstance(delegate::class.java.classLoader, arrayOf(CordaRPCOps::class.java), handler) as CordaRPCOps return Proxy.newProxyInstance(delegate::class.java.classLoader, arrayOf(InternalCordaRPCOps::class.java), handler) as InternalCordaRPCOps
} }
} }
private class ThreadContextAdjustingInvocationHandler(override val delegate: CordaRPCOps, private val classLoader: ClassLoader) : InvocationHandlerTemplate { private class ThreadContextAdjustingInvocationHandler(override val delegate: InternalCordaRPCOps, private val classLoader: ClassLoader) : InvocationHandlerTemplate {
override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?): Any? { override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?): Any? {
return executeWithThreadContextClassLoader(this.classLoader) { super.invoke(proxy, method, arguments) } return executeWithThreadContextClassLoader(this.classLoader) { super.invoke(proxy, method, arguments) }
} }

View File

@ -1,15 +1,19 @@
package net.corda.tools.shell package net.corda.tools.shell
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.type.TypeFactory
import com.google.common.io.Files import com.google.common.io.Files
import com.jcraft.jsch.ChannelExec import com.jcraft.jsch.ChannelExec
import com.jcraft.jsch.JSch import com.jcraft.jsch.JSch
import com.nhaarman.mockito_kotlin.any import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.doAnswer import com.nhaarman.mockito_kotlin.doAnswer
import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.mock
import net.corda.client.jackson.JacksonSupport
import net.corda.client.rpc.RPCException import net.corda.client.rpc.RPCException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.messaging.ClientRpcSslOptions import net.corda.core.messaging.ClientRpcSslOptions
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
@ -33,11 +37,13 @@ import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.bouncycastle.util.io.Streams import org.bouncycastle.util.io.Streams
import org.crsh.text.RenderPrintWriter import org.crsh.text.RenderPrintWriter
import org.junit.Before
import org.junit.Ignore import org.junit.Ignore
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import javax.security.auth.x500.X500Principal import javax.security.auth.x500.X500Principal
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
class InteractiveShellIntegrationTest { class InteractiveShellIntegrationTest {
@ -47,6 +53,13 @@ class InteractiveShellIntegrationTest {
private val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB") private val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
private lateinit var inputObjectMapper: ObjectMapper
@Before
fun setup() {
inputObjectMapper = objectMapperWithClassLoader(InteractiveShell.getCordappsClassloader())
}
@Test @Test
fun `shell should not log in with invalid credentials`() { fun `shell should not log in with invalid credentials`() {
val user = User("u", "p", setOf()) val user = User("u", "p", setOf())
@ -277,6 +290,30 @@ class InteractiveShellIntegrationTest {
assertThat(successful).isTrue() assertThat(successful).isTrue()
} }
@Test
fun `can run dumpCheckpoints`() {
val user = User("u", "p", setOf(all()))
driver(DriverParameters(notarySpecs = emptyList())) {
val nodeFuture = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user), startInSameProcess = true)
val node = nodeFuture.getOrThrow()
val conf = ShellConfiguration(commandsDirectory = Files.createTempDir().toPath(),
user = user.username, password = user.password,
hostAndPort = node.rpcAddress)
InteractiveShell.startShell(conf)
// setup and configure some mocks required by InteractiveShell.runFlowByNameFragment()
val output = mock<RenderPrintWriter> {
on { println(any<String>()) } doAnswer {
val line = it.arguments[0]
assertNotEquals("Please try 'man run' to learn what syntax is acceptable", line)
}
}
// can call without causing any errors, no output to easily check
InteractiveShell.runRPCFromString(
listOf("dumpCheckpoints"), output, mock(), node.rpc as InternalCordaRPCOps, inputObjectMapper)
}
}
@Test @Test
fun `shell should start flow with unique un-qualified class name`() { fun `shell should start flow with unique un-qualified class name`() {
val user = User("u", "p", setOf(all())) val user = User("u", "p", setOf(all()))
@ -372,6 +409,14 @@ class InteractiveShellIntegrationTest {
} }
assertThat(successful).isTrue() assertThat(successful).isTrue()
} }
private fun objectMapperWithClassLoader(classLoader: ClassLoader?): ObjectMapper {
val objectMapper = JacksonSupport.createNonRpcMapper()
val tf = TypeFactory.defaultInstance().withClassLoader(classLoader)
objectMapper.typeFactory = tf
return objectMapper
}
} }
@Suppress("UNUSED") @Suppress("UNUSED")

View File

@ -3,6 +3,7 @@ package net.corda.tools.shell;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import net.corda.client.jackson.StringToMethodCallParser; import net.corda.client.jackson.StringToMethodCallParser;
import net.corda.core.internal.messaging.InternalCordaRPCOps;
import net.corda.core.messaging.CordaRPCOps; import net.corda.core.messaging.CordaRPCOps;
import org.crsh.cli.Argument; import org.crsh.cli.Argument;
import org.crsh.cli.Command; import org.crsh.cli.Command;
@ -13,10 +14,7 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static java.util.Comparator.comparing; import static java.util.Comparator.comparing;
@ -38,21 +36,31 @@ public class RunShellCommand extends InteractiveShellCommand {
public Object main(InvocationContext<Map> context, public Object main(InvocationContext<Map> context,
@Usage("The command to run") @Argument(unquote = false) List<String> command) { @Usage("The command to run") @Argument(unquote = false) List<String> command) {
logger.info("Executing command \"run {}\",", (command != null) ? String.join(" ", command) : "<no arguments>"); logger.info("Executing command \"run {}\",", (command != null) ? String.join(" ", command) : "<no arguments>");
StringToMethodCallParser<CordaRPCOps> parser = new StringToMethodCallParser<>(CordaRPCOps.class, objectMapper(InteractiveShell.getCordappsClassloader()));
if (command == null) { if (command == null) {
emitHelp(context, parser); emitHelp(context);
return null; return null;
} }
return InteractiveShell.runRPCFromString(command, out, context, ops(), objectMapper(InteractiveShell.getCordappsClassloader())); return InteractiveShell.runRPCFromString(command, out, context, ops(), objectMapper(InteractiveShell.getCordappsClassloader()));
} }
private void emitHelp(InvocationContext<Map> context, StringToMethodCallParser<CordaRPCOps> parser) { private void emitHelp(InvocationContext<Map> context) {
// Sends data down the pipeline about what commands are available. CRaSH will render it nicely. // to handle the lack of working inheritance in [StringToMethodCallParser] two parsers are used
// Each element we emit is a map of column -> content. StringToMethodCallParser<CordaRPCOps> cordaRpcOpsParser =
Set<Map.Entry<String, String>> entries = parser.getAvailableCommands().entrySet(); new StringToMethodCallParser<>(
List<Map.Entry<String, String>> entryList = new ArrayList<>(entries); CordaRPCOps.class, objectMapper(InteractiveShell.getCordappsClassloader()));
StringToMethodCallParser<InternalCordaRPCOps> internalCordaRpcOpsParser =
new StringToMethodCallParser<>(
InternalCordaRPCOps.class, objectMapper(InteractiveShell.getCordappsClassloader()));
// Sends data down the pipeline about what commands are available. CRaSH will render it nicely.
// Each element we emit is a map of column -> content.
Set<Map.Entry<String, String>> entries = cordaRpcOpsParser.getAvailableCommands().entrySet();
Set<Map.Entry<String, String>> internalEntries = internalCordaRpcOpsParser.getAvailableCommands().entrySet();
Set<Map.Entry<String, String>> entrySet = new HashSet<>(entries);
entrySet.addAll(internalEntries);
List<Map.Entry<String, String>> entryList = new ArrayList<>(entrySet);
entryList.sort(comparing(Map.Entry::getKey)); entryList.sort(comparing(Map.Entry::getKey));
for (Map.Entry<String, String> entry : entryList) { for (Map.Entry<String, String> entry : entryList) {
// Skip these entries as they aren't really interesting for the user. // Skip these entries as they aren't really interesting for the user.

View File

@ -1,13 +1,13 @@
package net.corda.tools.shell package net.corda.tools.shell
import net.corda.core.messaging.CordaRPCOps import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.crsh.auth.AuthInfo import org.crsh.auth.AuthInfo
import org.crsh.auth.AuthenticationPlugin import org.crsh.auth.AuthenticationPlugin
import org.crsh.plugin.CRaSHPlugin import org.crsh.plugin.CRaSHPlugin
class CordaAuthenticationPlugin(private val rpcOps: (username: String, credential: String) -> CordaRPCOps) : CRaSHPlugin<AuthenticationPlugin<String>>(), AuthenticationPlugin<String> { class CordaAuthenticationPlugin(private val rpcOps: (username: String, credential: String) -> InternalCordaRPCOps) : CRaSHPlugin<AuthenticationPlugin<String>>(), AuthenticationPlugin<String> {
companion object { companion object {
private val logger = loggerFor<CordaAuthenticationPlugin>() private val logger = loggerFor<CordaAuthenticationPlugin>()

View File

@ -1,12 +1,12 @@
package net.corda.tools.shell package net.corda.tools.shell
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import net.corda.core.messaging.CordaRPCOps import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.tools.shell.InteractiveShell.createYamlInputMapper import net.corda.tools.shell.InteractiveShell.createYamlInputMapper
import net.corda.tools.shell.utlities.ANSIProgressRenderer import net.corda.tools.shell.utlities.ANSIProgressRenderer
import org.crsh.auth.AuthInfo import org.crsh.auth.AuthInfo
class CordaSSHAuthInfo(val successful: Boolean, val rpcOps: CordaRPCOps, val ansiProgressRenderer: ANSIProgressRenderer? = null, val isSsh: Boolean = false) : AuthInfo { class CordaSSHAuthInfo(val successful: Boolean, val rpcOps: InternalCordaRPCOps, val ansiProgressRenderer: ANSIProgressRenderer? = null, val isSsh: Boolean = false) : AuthInfo {
override fun isSuccessful(): Boolean = successful override fun isSuccessful(): Boolean = successful
val yamlInputMapper: ObjectMapper by lazy { val yamlInputMapper: ObjectMapper by lazy {

View File

@ -23,6 +23,7 @@ import net.corda.core.flows.StateMachineRunId
import net.corda.core.internal.* import net.corda.core.internal.*
import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.concurrent.doneFuture
import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.tools.shell.utlities.ANSIProgressRenderer import net.corda.tools.shell.utlities.ANSIProgressRenderer
import net.corda.tools.shell.utlities.StdoutANSIProgressRenderer import net.corda.tools.shell.utlities.StdoutANSIProgressRenderer
@ -73,8 +74,8 @@ import kotlin.concurrent.thread
object InteractiveShell { object InteractiveShell {
private val log = LoggerFactory.getLogger(javaClass) private val log = LoggerFactory.getLogger(javaClass)
private lateinit var rpcOps: (username: String, password: String) -> CordaRPCOps private lateinit var rpcOps: (username: String, password: String) -> InternalCordaRPCOps
private lateinit var ops: CordaRPCOps private lateinit var ops: InternalCordaRPCOps
private lateinit var rpcConn: AutoCloseable private lateinit var rpcConn: AutoCloseable
private var shell: Shell? = null private var shell: Shell? = null
private var classLoader: ClassLoader? = null private var classLoader: ClassLoader? = null
@ -104,7 +105,7 @@ object InteractiveShell {
classLoader = classLoader) classLoader = classLoader)
val connection = client.start(username, password) val connection = client.start(username, password)
rpcConn = connection rpcConn = connection
connection.proxy connection.proxy as InternalCordaRPCOps
} }
} }
_startShell(configuration, classLoader) _startShell(configuration, classLoader)
@ -488,7 +489,7 @@ object InteractiveShell {
} }
@JvmStatic @JvmStatic
fun runRPCFromString(input: List<String>, out: RenderPrintWriter, context: InvocationContext<out Any>, cordaRPCOps: CordaRPCOps, fun runRPCFromString(input: List<String>, out: RenderPrintWriter, context: InvocationContext<out Any>, cordaRPCOps: InternalCordaRPCOps,
inputObjectMapper: ObjectMapper): Any? { inputObjectMapper: ObjectMapper): Any? {
val cmd = input.joinToString(" ").trim { it <= ' ' } val cmd = input.joinToString(" ").trim { it <= ' ' }
if (cmd.startsWith("startflow", ignoreCase = true)) { if (cmd.startsWith("startflow", ignoreCase = true)) {
@ -504,7 +505,7 @@ object InteractiveShell {
var result: Any? = null var result: Any? = null
try { try {
InputStreamSerializer.invokeContext = context InputStreamSerializer.invokeContext = context
val parser = StringToMethodCallParser(CordaRPCOps::class.java, inputObjectMapper) val parser = StringToMethodCallParser(InternalCordaRPCOps::class.java, inputObjectMapper)
val call = parser.parse(cordaRPCOps, cmd) val call = parser.parse(cordaRPCOps, cmd)
result = call.call() result = call.call()
if (result != null && result !== kotlin.Unit && result !is Void) { if (result != null && result !== kotlin.Unit && result !is Void) {

View File

@ -1,20 +1,20 @@
package net.corda.tools.shell package net.corda.tools.shell
import net.corda.core.messaging.CordaRPCOps import net.corda.core.internal.messaging.InternalCordaRPCOps
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Proxy import java.lang.reflect.Proxy
fun makeRPCOps(getCordaRPCOps: (username: String, credential: String) -> CordaRPCOps, username: String, credential: String): CordaRPCOps { fun makeRPCOps(getCordaRPCOps: (username: String, credential: String) -> InternalCordaRPCOps, username: String, credential: String): InternalCordaRPCOps {
val cordaRPCOps: CordaRPCOps by lazy { val cordaRPCOps: InternalCordaRPCOps by lazy {
getCordaRPCOps(username, credential) getCordaRPCOps(username, credential)
} }
return Proxy.newProxyInstance(CordaRPCOps::class.java.classLoader, arrayOf(CordaRPCOps::class.java), { _, method, args -> return Proxy.newProxyInstance(InternalCordaRPCOps::class.java.classLoader, arrayOf(InternalCordaRPCOps::class.java)) { _, method, args ->
try { try {
method.invoke(cordaRPCOps, *(args ?: arrayOf())) method.invoke(cordaRPCOps, *(args ?: arrayOf()))
} catch (e: InvocationTargetException) { } catch (e: InvocationTargetException) {
// Unpack exception. // Unpack exception.
throw e.targetException throw e.targetException
} }
}) as CordaRPCOps } as InternalCordaRPCOps
} }

View File

@ -18,7 +18,7 @@ import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.openFuture
import net.corda.core.messaging.CordaRPCOps import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.messaging.FlowProgressHandleImpl import net.corda.core.messaging.FlowProgressHandleImpl
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
@ -41,7 +41,7 @@ import kotlin.test.assertFailsWith
class InteractiveShellTest { class InteractiveShellTest {
lateinit var inputObjectMapper: ObjectMapper lateinit var inputObjectMapper: ObjectMapper
lateinit var cordaRpcOps: CordaRPCOps lateinit var cordaRpcOps: InternalCordaRPCOps
lateinit var invocationContext: InvocationContext<Map<Any, Any>> lateinit var invocationContext: InvocationContext<Map<Any, Any>>
lateinit var printWriter: RenderPrintWriter lateinit var printWriter: RenderPrintWriter