mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
CORDA-3959: Eliminate InternalCordaRPCOps (#6600)
* CORDA-3959: Make `ThreadContextAdjustingRpcOpsProxy` flexible for `RPCOps` it uses * CORDA-3959: More changes towards supporting multiple `RPCOps` implementations * CORDA-3959: Detekt baseline update * CORDA-3959: Integration test compilation fix * CORDA-3959: Introduce `CheckpointRPCOpsImpl` and wire it on * CORDA-3959: Use multiple RPCOps interfaces in the shell commands * CORDA-3959: Detekt baseline update * CORDA-3959: Update RPCPermissionsTests * CORDA-3959: Update RPCSecurityManagerTest * CORDA-3959: Remove deprecated marker and rename the property * CORDA-3959: Detekt baseline * CORDA-3959: Introduce AttachmentTrustInfoRPCOpsImpl and wire it on * CORDA-3959: Delete `InternalCordaRPCOps` * CORDA-3959: Detekt baseline update * CORDA-3959: Rename `CheckpointRPCOps` to `FlowManagerRPCOps`
This commit is contained in:
parent
c12c582eb4
commit
b81eb1559d
@ -12,7 +12,6 @@ import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.PLATFORM_VERSION
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.internal.createInstancesOfClassesImplementing
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import net.corda.core.messaging.ClientRpcSslOptions
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.serialization.SerializationCustomSerializer
|
||||
@ -494,7 +493,7 @@ class CordaRPCClient private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRpcClient(): RPCClient<InternalCordaRPCOps> {
|
||||
private fun getRpcClient(): RPCClient<CordaRPCOps> {
|
||||
return when {
|
||||
// Client->RPC broker
|
||||
haAddressPool.isEmpty() -> RPCClient(
|
||||
@ -619,7 +618,7 @@ class CordaRPCClient private constructor(
|
||||
)
|
||||
} else {
|
||||
CordaRPCConnection(getRpcClient().start(
|
||||
InternalCordaRPCOps::class.java,
|
||||
CordaRPCOps::class.java,
|
||||
username,
|
||||
password,
|
||||
externalTrace,
|
||||
|
@ -19,7 +19,6 @@ import net.corda.core.internal.LazyStickyPool
|
||||
import net.corda.core.internal.LifeCycle
|
||||
import net.corda.core.internal.NamedCacheFactory
|
||||
import net.corda.core.internal.ThreadBox
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import net.corda.core.internal.times
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.RPCOps
|
||||
@ -363,10 +362,10 @@ internal class RPCClientProxyHandler(
|
||||
private fun produceMethodFullyQualifiedName(method: Method) : String {
|
||||
/*
|
||||
* Until version 4.3, rpc calls did not include class names.
|
||||
* Up to this version, only CordaRPCOps and InternalCordaRPCOps were supported.
|
||||
* Up to this version, only CordaRPCOps was supported.
|
||||
* So, for these classes only methods are sent across the wire to preserve backwards compatibility.
|
||||
*/
|
||||
return if (CordaRPCOps::class.java == rpcOpsClass || InternalCordaRPCOps::class.java == rpcOpsClass) {
|
||||
return if (CordaRPCOps::class.java == rpcOpsClass) {
|
||||
method.name
|
||||
} else {
|
||||
rpcOpsClass.name + CLASS_METHOD_DIVIDER + method.name
|
||||
|
@ -19,7 +19,6 @@ import net.corda.client.rpc.internal.ReconnectingCordaRPCOps.ReconnectingRPCConn
|
||||
import net.corda.client.rpc.internal.ReconnectingCordaRPCOps.ReconnectingRPCConnection.CurrentState.UNCONNECTED
|
||||
import net.corda.client.rpc.reconnect.CouldNotStartFlowException
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import net.corda.core.internal.min
|
||||
import net.corda.core.internal.times
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
@ -65,7 +64,7 @@ import java.util.concurrent.TimeUnit
|
||||
// ReconnectingObservables and other things can attach themselves as listeners for reconnect events.
|
||||
class ReconnectingCordaRPCOps private constructor(
|
||||
val reconnectingRPCConnection: ReconnectingRPCConnection
|
||||
) : InternalCordaRPCOps by proxy(reconnectingRPCConnection) {
|
||||
) : CordaRPCOps by proxy(reconnectingRPCConnection) {
|
||||
constructor(
|
||||
nodeHostAndPorts: List<NetworkHostAndPort>,
|
||||
username: String,
|
||||
@ -86,11 +85,11 @@ class ReconnectingCordaRPCOps private constructor(
|
||||
observersPool))
|
||||
private companion object {
|
||||
private val log = contextLogger()
|
||||
private fun proxy(reconnectingRPCConnection: ReconnectingRPCConnection): InternalCordaRPCOps {
|
||||
private fun proxy(reconnectingRPCConnection: ReconnectingRPCConnection): CordaRPCOps {
|
||||
return Proxy.newProxyInstance(
|
||||
this::class.java.classLoader,
|
||||
arrayOf(InternalCordaRPCOps::class.java),
|
||||
ErrorInterceptingHandler(reconnectingRPCConnection)) as InternalCordaRPCOps
|
||||
arrayOf(CordaRPCOps::class.java),
|
||||
ErrorInterceptingHandler(reconnectingRPCConnection)) as CordaRPCOps
|
||||
}
|
||||
}
|
||||
private val retryFlowsPool = Executors.newScheduledThreadPool(1)
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.corda.client.rpc
|
||||
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.node.internal.rpc.proxies.RpcAuthHelper.methodFullName
|
||||
import net.corda.node.services.rpc.rpcContext
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.RPCDriverDSL
|
||||
@ -25,16 +27,17 @@ class RPCPermissionsTests : AbstractRPCTest() {
|
||||
fun validatePermission(method: String, target: String? = null)
|
||||
}
|
||||
|
||||
class TestOpsImpl : TestOps {
|
||||
private class TestOpsImpl : TestOps {
|
||||
override val protocolVersion = 1000
|
||||
override fun validatePermission(method: String, target: String?) {
|
||||
val methodFullName = methodFullName(CordaRPCOps::class.java, method)
|
||||
val authorized = if (target == null) {
|
||||
rpcContext().isPermitted(method)
|
||||
rpcContext().isPermitted(methodFullName)
|
||||
} else {
|
||||
rpcContext().isPermitted(method, target)
|
||||
rpcContext().isPermitted(methodFullName, target)
|
||||
}
|
||||
if (!authorized) {
|
||||
throw PermissionException("RPC user not authorized")
|
||||
throw PermissionException("RPC user not authorized for: $method")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ class ContractUpgradeFlowRPCTest : WithContracts, WithFinality {
|
||||
return startRpcClient<CordaRPCOps>(
|
||||
rpcAddress = startRpcServer(
|
||||
rpcUser = user,
|
||||
ops = node.rpcOps
|
||||
ops = node.cordaRPCOps
|
||||
).get().broker.hostAndPort!!,
|
||||
username = user.username,
|
||||
password = user.password
|
||||
|
@ -0,0 +1,11 @@
|
||||
package net.corda.core.internal.messaging
|
||||
|
||||
import net.corda.core.internal.AttachmentTrustInfo
|
||||
import net.corda.core.messaging.RPCOps
|
||||
|
||||
interface AttachmentTrustInfoRPCOps : RPCOps {
|
||||
/**
|
||||
* Get all the attachment trust information
|
||||
*/
|
||||
val attachmentTrustInfos: List<AttachmentTrustInfo>
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package net.corda.core.internal.messaging
|
||||
|
||||
import net.corda.core.messaging.RPCOps
|
||||
|
||||
/**
|
||||
* RPC operations to perform operations related to flows including management of associated persistent states like checkpoints.
|
||||
*/
|
||||
interface FlowManagerRPCOps : RPCOps {
|
||||
/**
|
||||
* Dump all the current flow checkpoints as JSON into a zip file in the node's log directory.
|
||||
*/
|
||||
fun dumpCheckpoints()
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package net.corda.core.internal.messaging
|
||||
|
||||
import net.corda.core.internal.AttachmentTrustInfo
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
|
||||
/**
|
||||
* Contains internal RPC functions that should not be publicly exposed in [CordaRPCOps]
|
||||
*/
|
||||
interface InternalCordaRPCOps : CordaRPCOps {
|
||||
|
||||
/** Dump all the current flow checkpoints as JSON into a zip file in the node's log directory. */
|
||||
fun dumpCheckpoints()
|
||||
|
||||
/** Get all attachment trust information */
|
||||
val attachmentTrustInfos: List<AttachmentTrustInfo>
|
||||
}
|
@ -151,7 +151,7 @@
|
||||
<ID>ComplexMethod:NetworkRegistrationHelper.kt$NetworkRegistrationHelper$ private fun pollServerForCertificates(requestId: String): List<X509Certificate></ID>
|
||||
<ID>ComplexMethod:NewTransaction.kt$NewTransaction$fun show(window: Window)</ID>
|
||||
<ID>ComplexMethod:NewTransaction.kt$NewTransaction$private fun newTransactionDialog(window: Window)</ID>
|
||||
<ID>ComplexMethod:Node.kt$Node$override fun startMessagingService(rpcOps: RPCOps, nodeInfo: NodeInfo, myNotaryIdentity: PartyAndCertificate?, networkParameters: NetworkParameters)</ID>
|
||||
<ID>ComplexMethod:Node.kt$Node$override fun startMessagingService(rpcOps: List<RPCOps>, nodeInfo: NodeInfo, myNotaryIdentity: PartyAndCertificate?, networkParameters: NetworkParameters)</ID>
|
||||
<ID>ComplexMethod:NodeNamedCache.kt$DefaultNamedCacheFactory$open protected fun <K, V> configuredForNamed(caffeine: Caffeine<K, V>, name: String): Caffeine<K, V></ID>
|
||||
<ID>ComplexMethod:NodeVaultService.kt$NodeVaultService$@Throws(VaultQueryException::class) private fun <T : ContractState> _queryBy(criteria: QueryCriteria, paging_: PageSpecification, sorting: Sort, contractStateType: Class<out T>, skipPagingChecks: Boolean): Vault.Page<T></ID>
|
||||
<ID>ComplexMethod:NodeVaultService.kt$NodeVaultService$private fun makeUpdates(batch: Iterable<CoreTransaction>, statesToRecord: StatesToRecord, previouslySeen: Boolean): List<Vault.Update<ContractState>></ID>
|
||||
@ -159,15 +159,12 @@
|
||||
<ID>ComplexMethod:ObjectDiffer.kt$ObjectDiffer$fun diff(a: Any?, b: Any?): DiffTree?</ID>
|
||||
<ID>ComplexMethod:Obligation.kt$Obligation$override fun verify(tx: LedgerTransaction)</ID>
|
||||
<ID>ComplexMethod:QuasarInstrumentationHook.kt$QuasarInstrumentationHookAgent.Companion$@JvmStatic fun premain(argumentsString: String?, instrumentation: Instrumentation)</ID>
|
||||
<ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$ private fun close(notify: Boolean = true)</ID>
|
||||
<ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$// The handler for Artemis messages. private fun artemisMessageHandler(message: ClientMessage)</ID>
|
||||
<ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$// This is the general function that transforms a client side RPC to internal Artemis messages. override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?): Any?</ID>
|
||||
<ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$private fun attemptReconnect()</ID>
|
||||
<ID>ComplexMethod:RPCServer.kt$RPCServer$private fun clientArtemisMessageHandler(artemisMessage: ClientMessage)</ID>
|
||||
<ID>ComplexMethod:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ReconnectingRPCConnection$ private tailrec fun establishConnectionWithRetry( retryInterval: Duration, roundRobinIndex: Int = 0, retries: Int = -1 ): CordaRPCConnection?</ID>
|
||||
<ID>ComplexMethod:RemoteTypeCarpenter.kt$SchemaBuildingRemoteTypeCarpenter$override fun carpent(typeInformation: RemoteTypeInformation): Type</ID>
|
||||
<ID>ComplexMethod:SchemaMigration.kt$SchemaMigration$ private fun migrateOlderDatabaseToUseLiquibase(existingCheckpoints: Boolean): Boolean</ID>
|
||||
<ID>ComplexMethod:SchemaMigration.kt$SchemaMigration$private fun doRunMigration( run: Boolean, check: Boolean, existingCheckpoints: Boolean? = null )</ID>
|
||||
<ID>ComplexMethod:SendTransactionFlow.kt$DataVendingFlow$@Suspendable override fun call(): Void?</ID>
|
||||
<ID>ComplexMethod:ShellCmdLineOptions.kt$ShellCmdLineOptions$private fun toConfigFile(): Config</ID>
|
||||
<ID>ComplexMethod:StartedFlowTransition.kt$StartedFlowTransition$override fun transition(): TransitionResult</ID>
|
||||
@ -176,7 +173,6 @@
|
||||
<ID>ComplexMethod:TlsDiffAlgorithmsTest.kt$TlsDiffAlgorithmsTest$@Test(timeout=300_000) fun testClientServerTlsExchange()</ID>
|
||||
<ID>ComplexMethod:TlsDiffProtocolsTest.kt$TlsDiffProtocolsTest$@Test(timeout=300_000) fun testClientServerTlsExchange()</ID>
|
||||
<ID>ComplexMethod:TransactionUtils.kt$ fun createComponentGroups(inputs: List<StateRef>, outputs: List<TransactionState<ContractState>>, commands: List<Command<*>>, attachments: List<SecureHash>, notary: Party?, timeWindow: TimeWindow?, references: List<StateRef>, networkParametersHash: SecureHash?): List<ComponentGroup></ID>
|
||||
<ID>ComplexMethod:TransitionExecutorImpl.kt$TransitionExecutorImpl$@Suppress("NestedBlockDepth", "ReturnCount") @Suspendable override fun executeTransition( fiber: FlowFiber, previousState: StateMachineState, event: Event, transition: TransitionResult, actionExecutor: ActionExecutor ): Pair<FlowContinuation, StateMachineState></ID>
|
||||
<ID>ComplexMethod:TypeModellingFingerPrinter.kt$FingerPrintingState$// For a type we haven't seen before, determine the correct path depending on the type of type it is. private fun fingerprintNewType(type: LocalTypeInformation)</ID>
|
||||
<ID>ComplexMethod:UniversalContract.kt$UniversalContract$override fun verify(tx: LedgerTransaction)</ID>
|
||||
<ID>ComplexMethod:Util.kt$fun <T> debugCompare(perLeft: Perceivable<T>, perRight: Perceivable<T>)</ID>
|
||||
@ -612,8 +608,6 @@
|
||||
<ID>LongParameterList:AbstractCashSelection.kt$AbstractCashSelection$(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?, onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>, withResultSet: (ResultSet) -> Boolean)</ID>
|
||||
<ID>LongParameterList:AbstractCashSelection.kt$AbstractCashSelection$(services: ServiceHub, amount: Amount<Currency>, lockId: UUID, notary: Party?, onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>, stateAndRefs: MutableList<StateAndRef<Cash.State>>)</ID>
|
||||
<ID>LongParameterList:AbstractCashSelection.kt$AbstractCashSelection$(services: ServiceHub, amount: Amount<Currency>, onlyFromIssuerParties: Set<AbstractParty> = emptySet(), notary: Party? = null, lockId: UUID, withIssuerRefs: Set<OpaqueBytes> = emptySet())</ID>
|
||||
<ID>LongParameterList:AbstractNode.kt$(databaseConfig: DatabaseConfig, wellKnownPartyFromX500Name: (CordaX500Name) -> Party?, wellKnownPartyFromAnonymous: (AbstractParty) -> Party?, schemaService: SchemaService, hikariProperties: Properties, cacheFactory: NamedCacheFactory, customClassLoader: ClassLoader?)</ID>
|
||||
<ID>LongParameterList:AbstractNode.kt$(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set<MappedSchema>, metricRegistry: MetricRegistry? = null, cordappLoader: CordappLoader? = null, currentDir: Path? = null, ourName: CordaX500Name)</ID>
|
||||
<ID>LongParameterList:ArtemisMessagingServer.kt$ArtemisMessagingServer$(name: String, send: Boolean = false, consume: Boolean = false, createDurableQueue: Boolean = false, deleteDurableQueue: Boolean = false, createNonDurableQueue: Boolean = false, deleteNonDurableQueue: Boolean = false, manage: Boolean = false, browse: Boolean = false)</ID>
|
||||
<ID>LongParameterList:ArtemisRpcBroker.kt$ArtemisRpcBroker.Companion$(configuration: MutualSslConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, securityManager: RPCSecurityManager, maxMessageSize: Int, journalBufferTimeout: Int?, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean)</ID>
|
||||
<ID>LongParameterList:ArtemisRpcBroker.kt$ArtemisRpcBroker.Companion$(configuration: MutualSslConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, sslOptions: BrokerRpcSslOptions, securityManager: RPCSecurityManager, maxMessageSize: Int, journalBufferTimeout: Int?, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean)</ID>
|
||||
@ -655,13 +649,11 @@
|
||||
<ID>LongParameterList:IdenticonRenderer.kt$IdenticonRenderer$(g: GraphicsContext, x: Double, y: Double, patchIndex: Int, turn: Int, patchSize: Double, _invert: Boolean, color: PatchColor)</ID>
|
||||
<ID>LongParameterList:Injectors.kt$( metricRegistry: MetricRegistry, parallelism: Int, overallDuration: Duration, injectionRate: Rate, queueSizeMetricName: String = "QueueSize", workDurationMetricName: String = "WorkDuration", work: () -> Unit )</ID>
|
||||
<ID>LongParameterList:InteractiveShell.kt$InteractiveShell$(nameFragment: String, inputData: String, output: RenderPrintWriter, rpcOps: CordaRPCOps, ansiProgressRenderer: ANSIProgressRenderer, inputObjectMapper: ObjectMapper = createYamlInputMapper(rpcOps))</ID>
|
||||
<ID>LongParameterList:InternalTestUtils.kt$(hikariProperties: Properties, databaseConfig: DatabaseConfig, wellKnownPartyFromX500Name: (CordaX500Name) -> Party?, wellKnownPartyFromAnonymous: (AbstractParty) -> Party?, schemaService: SchemaService = NodeSchemaService(), internalSchemas: Set<MappedSchema> = NodeSchemaService().internalSchemas(), cacheFactory: NamedCacheFactory = TestingNamedCacheFactory(), ourName: CordaX500Name = TestIdentity(ALICE_NAME, 70).name)</ID>
|
||||
<ID>LongParameterList:InternalTestUtils.kt$(inputs: List<StateRef>, attachments: List<SecureHash>, outputs: List<TransactionState<*>>, commands: List<Command<*>>, notary: Party?, timeWindow: TimeWindow?, privacySalt: PrivacySalt = PrivacySalt())</ID>
|
||||
<ID>LongParameterList:JarSignatureTestUtils.kt$JarSignatureTestUtils$(alias: String = "Test", storePassword: String = "secret!", name: String = CODE_SIGNER.toString(), keyalg: String = "RSA", keyPassword: String = storePassword, storeName: String = "_teststore")</ID>
|
||||
<ID>LongParameterList:MockServices.kt$MockServices.Companion$( cordappLoader: CordappLoader, identityService: IdentityService, networkParameters: NetworkParameters, initialIdentity: TestIdentity, moreKeys: Set<KeyPair>, keyManagementService: KeyManagementService, schemaService: SchemaService, persistence: CordaPersistence )</ID>
|
||||
<ID>LongParameterList:MockServices.kt$MockServices.Companion$( cordappPackages: List<String>, initialIdentity: TestIdentity, networkParameters: NetworkParameters = testNetworkParameters(modifiedTime = Instant.MIN), moreKeys: Set<KeyPair>, moreIdentities: Set<PartyAndCertificate>, cacheFactory: TestingNamedCacheFactory = TestingNamedCacheFactory() )</ID>
|
||||
<ID>LongParameterList:NetworkBootstrapperTest.kt$NetworkBootstrapperTest$(copyCordapps: CopyCordapps = CopyCordapps.FirstRunOnly, packageOwnership: Map<String, PublicKey>? = emptyMap(), minimumPlatformVerison: Int? = PLATFORM_VERSION, maxMessageSize: Int? = DEFAULT_MAX_MESSAGE_SIZE, maxTransactionSize: Int? = DEFAULT_MAX_TRANSACTION_SIZE, eventHorizon: Duration? = 30.days)</ID>
|
||||
<ID>LongParameterList:NetworkMapUpdater.kt$NetworkMapUpdater$(trustRoot: X509Certificate, currentParametersHash: SecureHash, ourNodeInfo: SignedNodeInfo, networkParameters: NetworkParameters, keyManagementService: KeyManagementService, networkParameterAcceptanceSettings: NetworkParameterAcceptanceSettings)</ID>
|
||||
<ID>LongParameterList:NetworkParameters.kt$NetworkParameters$(minimumPlatformVersion: Int = this.minimumPlatformVersion, notaries: List<NotaryInfo> = this.notaries, maxMessageSize: Int = this.maxMessageSize, maxTransactionSize: Int = this.maxTransactionSize, modifiedTime: Instant = this.modifiedTime, epoch: Int = this.epoch, whitelistedContractImplementations: Map<String, List<AttachmentId>> = this.whitelistedContractImplementations )</ID>
|
||||
<ID>LongParameterList:NetworkParameters.kt$NetworkParameters$(minimumPlatformVersion: Int = this.minimumPlatformVersion, notaries: List<NotaryInfo> = this.notaries, maxMessageSize: Int = this.maxMessageSize, maxTransactionSize: Int = this.maxTransactionSize, modifiedTime: Instant = this.modifiedTime, epoch: Int = this.epoch, whitelistedContractImplementations: Map<String, List<AttachmentId>> = this.whitelistedContractImplementations, eventHorizon: Duration = this.eventHorizon )</ID>
|
||||
<ID>LongParameterList:NodeParameters.kt$NodeParameters$( providedName: CordaX500Name?, rpcUsers: List<User>, verifierType: VerifierType, customOverrides: Map<String, Any?>, startInSameProcess: Boolean?, maximumHeapSize: String )</ID>
|
||||
@ -770,6 +762,7 @@
|
||||
<ID>MagicNumber:CordaRPCClient.kt$CordaRPCClient$128</ID>
|
||||
<ID>MagicNumber:CordaRPCClient.kt$CordaRPCClientConfiguration$3</ID>
|
||||
<ID>MagicNumber:CordaRPCClient.kt$CordaRPCClientConfiguration$5</ID>
|
||||
<ID>MagicNumber:CordaSSHAuthInfo.kt$CordaSSHAuthInfo$10</ID>
|
||||
<ID>MagicNumber:CordaSecurityProvider.kt$CordaSecurityProvider$0.1</ID>
|
||||
<ID>MagicNumber:Crypto.kt$Crypto$2048</ID>
|
||||
<ID>MagicNumber:Crypto.kt$Crypto$256</ID>
|
||||
@ -1080,6 +1073,7 @@
|
||||
<ID>MagicNumber:RPCDriver.kt$RandomRpcUser.Companion$100</ID>
|
||||
<ID>MagicNumber:RPCDriver.kt$RandomRpcUser.Companion$3</ID>
|
||||
<ID>MagicNumber:RPCDriver.kt$RandomRpcUser.Companion$4</ID>
|
||||
<ID>MagicNumber:RPCPermissionResolver.kt$RPCPermissionResolver$20</ID>
|
||||
<ID>MagicNumber:RPCServer.kt$RPCServer$5</ID>
|
||||
<ID>MagicNumber:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps$4</ID>
|
||||
<ID>MagicNumber:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ErrorInterceptingHandler$1000</ID>
|
||||
@ -1243,8 +1237,7 @@
|
||||
<ID>SpreadOperator:AssertingTestDatabaseContext.kt$AssertingTestDatabaseContext$(*expectedScripts)</ID>
|
||||
<ID>SpreadOperator:AttachmentDemo.kt$(*args)</ID>
|
||||
<ID>SpreadOperator:AttachmentsClassLoaderStaticContractTests.kt$AttachmentsClassLoaderStaticContractTests$(*packages.toTypedArray())</ID>
|
||||
<ID>SpreadOperator:AuthenticatedRpcOpsProxy.kt$(methodName, *(args.map(Class<*>::getName).toTypedArray()))</ID>
|
||||
<ID>SpreadOperator:AuthenticatedRpcOpsProxy.kt$AuthenticatedRpcOpsProxy$(logicType, *args)</ID>
|
||||
<ID>SpreadOperator:AuthenticatedRpcOpsProxy.kt$AuthenticatedRpcOpsProxy.PermissionsEnforcingInvocationHandler$(methodFullName(method), *(args.map(Class<*>::getName).toTypedArray()))</ID>
|
||||
<ID>SpreadOperator:AzureInstantiator.kt$AzureInstantiator$(*portsToOpen.toIntArray())</ID>
|
||||
<ID>SpreadOperator:ByteArrays.kt$OpaqueBytes.Companion$(*b)</ID>
|
||||
<ID>SpreadOperator:CertificateRevocationListNodeTests.kt$CertificateRevocationListNodeTests$(newNodeCert, INTERMEDIATE_CA.certificate, *nodeKeyStore.query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) }.drop(2).toTypedArray())</ID>
|
||||
@ -1350,8 +1343,7 @@
|
||||
<ID>SpreadOperator:PropertyValidationTest.kt$PropertyValidationTest$(*key.split(".").toTypedArray(), nestedKey)</ID>
|
||||
<ID>SpreadOperator:RPCClient.kt$RPCClient$(*haPoolTransportConfigurations.toTypedArray())</ID>
|
||||
<ID>SpreadOperator:RPCDriver.kt$RandomRpcUser.Companion$(handle.proxy, *arguments.toTypedArray())</ID>
|
||||
<ID>SpreadOperator:RPCOpsWithContext.kt$(cordaRPCOps, *(args ?: arrayOf()))</ID>
|
||||
<ID>SpreadOperator:RPCSecurityManagerTest.kt$RPCSecurityManagerTest$(request.first(), *args)</ID>
|
||||
<ID>SpreadOperator:RPCSecurityManagerTest.kt$RPCSecurityManagerTest$(methodName, *args)</ID>
|
||||
<ID>SpreadOperator:RPCServer.kt$RPCServer$(invocationTarget.instance, *arguments.toTypedArray())</ID>
|
||||
<ID>SpreadOperator:ReactiveArtemisConsumer.kt$ReactiveArtemisConsumer.Companion$(queueName, *queueNames)</ID>
|
||||
<ID>SpreadOperator:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ErrorInterceptingHandler$(reconnectingRPCConnection.proxy, *(args ?: emptyArray()))</ID>
|
||||
@ -1379,7 +1371,6 @@
|
||||
<ID>SpreadOperator:X509Utilities.kt$X509Utilities$(*certificates)</ID>
|
||||
<ID>ThrowsCount:AMQPTypeIdentifierParser.kt$AMQPTypeIdentifierParser$// Make sure our inputs aren't designed to blow things up. private fun validate(typeString: String)</ID>
|
||||
<ID>ThrowsCount:AbstractNode.kt$AbstractNode$private fun installCordaServices()</ID>
|
||||
<ID>ThrowsCount:AbstractNode.kt$fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set<MappedSchema>, metricRegistry: MetricRegistry? = null, cordappLoader: CordappLoader? = null, currentDir: Path? = null, ourName: CordaX500Name)</ID>
|
||||
<ID>ThrowsCount:ArtemisMessagingServer.kt$ArtemisMessagingServer$// TODO: Maybe wrap [IOException] on a key store load error so that it's clearly splitting key store loading from // Artemis IO errors @Throws(IOException::class, AddressBindingException::class, KeyStoreException::class) private fun configureAndStartServer()</ID>
|
||||
<ID>ThrowsCount:BrokerJaasLoginModule.kt$BaseBrokerJaasLoginModule$@Suppress("DEPRECATION") // should use java.security.cert.X509Certificate protected fun getUsernamePasswordAndCerts(): Triple<String, String, Array<javax.security.cert.X509Certificate>?></ID>
|
||||
<ID>ThrowsCount:CheckpointVerifier.kt$CheckpointVerifier$ fun verifyCheckpointsCompatible( checkpointStorage: CheckpointStorage, currentCordapps: List<Cordapp>, platformVersion: Int, serviceHub: ServiceHub, tokenizableServices: List<Any> )</ID>
|
||||
@ -1403,7 +1394,6 @@
|
||||
<ID>ThrowsCount:PropertyDescriptor.kt$PropertyDescriptor$ fun validate()</ID>
|
||||
<ID>ThrowsCount:RPCApi.kt$RPCApi.ServerToClient.Companion$fun fromClientMessage(context: SerializationContext, message: ClientMessage): ServerToClient</ID>
|
||||
<ID>ThrowsCount:RPCServer.kt$RPCServer$private fun invokeRpc(context: RpcAuthContext, inMethodName: String, arguments: List<Any?>): Try<Any></ID>
|
||||
<ID>ThrowsCount:SchemaMigration.kt$SchemaMigration$private fun doRunMigration( run: Boolean, check: Boolean, existingCheckpoints: Boolean? = null )</ID>
|
||||
<ID>ThrowsCount:ServicesForResolutionImpl.kt$ServicesForResolutionImpl$// We may need to recursively chase transactions if there are notary changes. fun inner(stateRef: StateRef, forContractClassName: String?): Attachment</ID>
|
||||
<ID>ThrowsCount:SignedNodeInfo.kt$SignedNodeInfo$// TODO Add root cert param (or TrustAnchor) to make sure all the identities belong to the same root fun verified(): NodeInfo</ID>
|
||||
<ID>ThrowsCount:SignedTransaction.kt$SignedTransaction$@DeleteForDJVM private fun resolveAndCheckNetworkParameters(services: ServiceHub)</ID>
|
||||
@ -1466,7 +1456,6 @@
|
||||
<ID>TooGenericExceptionCaught:Eventually.kt$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:Expect.kt$exception: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:Explorer.kt$Explorer$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:FiberDeserializationCheckingInterceptor.kt$FiberDeserializationChecker$exception: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:FinanceJSONSupport.kt$CalendarDeserializer$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:FlowHandle.kt$FlowProgressHandleImpl$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:FlowMessaging.kt$FlowMessagingImpl$exception: Exception</ID>
|
||||
@ -1541,6 +1530,7 @@
|
||||
<ID>TooGenericExceptionCaught:RPCClient.kt$RPCClient$exception: Throwable</ID>
|
||||
<ID>TooGenericExceptionCaught:RPCClientProxyHandler.kt$RPCClientProxyHandler$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:RPCClientProxyHandler.kt$RPCClientProxyHandler$e: RuntimeException</ID>
|
||||
<ID>TooGenericExceptionCaught:RPCPermissionResolver.kt$RPCPermissionResolver.InterfaceMethodMapCacheLoader$ex: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:RPCServer.kt$RPCServer$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:RPCServer.kt$RPCServer$exception: Throwable</ID>
|
||||
<ID>TooGenericExceptionCaught:RPCServer.kt$RPCServer$throwable: Throwable</ID>
|
||||
@ -1630,7 +1620,7 @@
|
||||
<ID>TooManyFunctions:ContractUpgradeTransactions.kt$ContractUpgradeLedgerTransaction : FullTransactionTransactionWithSignatures</ID>
|
||||
<ID>TooManyFunctions:CordaRPCOps.kt$CordaRPCOps : RPCOps</ID>
|
||||
<ID>TooManyFunctions:CordaRPCOps.kt$net.corda.core.messaging.CordaRPCOps.kt</ID>
|
||||
<ID>TooManyFunctions:CordaRPCOpsImpl.kt$CordaRPCOpsImpl : InternalCordaRPCOpsAutoCloseable</ID>
|
||||
<ID>TooManyFunctions:CordaRPCOpsImpl.kt$CordaRPCOpsImpl : CordaRPCOpsAutoCloseable</ID>
|
||||
<ID>TooManyFunctions:Crypto.kt$Crypto</ID>
|
||||
<ID>TooManyFunctions:CryptoUtils.kt$net.corda.core.crypto.CryptoUtils.kt</ID>
|
||||
<ID>TooManyFunctions:Currencies.kt$net.corda.finance.Currencies.kt</ID>
|
||||
@ -1663,8 +1653,6 @@
|
||||
<ID>TooManyFunctions:P2PMessagingClient.kt$P2PMessagingClient : SingletonSerializeAsTokenMessagingServiceAddressToArtemisQueueResolverServiceStateSupport</ID>
|
||||
<ID>TooManyFunctions:PathUtils.kt$net.corda.core.internal.PathUtils.kt</ID>
|
||||
<ID>TooManyFunctions:Perceivable.kt$net.corda.finance.contracts.universal.Perceivable.kt</ID>
|
||||
<ID>TooManyFunctions:PersistentIdentityService.kt$PersistentIdentityService : SingletonSerializeAsTokenIdentityServiceInternal</ID>
|
||||
<ID>TooManyFunctions:PersistentNetworkMapCache.kt$PersistentNetworkMapCache : NetworkMapCacheInternalSingletonSerializeAsToken</ID>
|
||||
<ID>TooManyFunctions:PortfolioApi.kt$PortfolioApi</ID>
|
||||
<ID>TooManyFunctions:PropertyDescriptor.kt$net.corda.serialization.internal.amqp.PropertyDescriptor.kt</ID>
|
||||
<ID>TooManyFunctions:QueryCriteria.kt$QueryCriteria$VaultQueryCriteria : CommonQueryCriteria</ID>
|
||||
@ -1700,8 +1688,6 @@
|
||||
<ID>UnusedImports:Amount.kt$import net.corda.core.crypto.CompositeKey</ID>
|
||||
<ID>UnusedImports:Amount.kt$import net.corda.core.identity.Party</ID>
|
||||
<ID>UnusedImports:DummyLinearStateSchemaV1.kt$import net.corda.core.contracts.ContractState</ID>
|
||||
<ID>UnusedImports:InternalTestUtils.kt$import java.nio.file.Files</ID>
|
||||
<ID>UnusedImports:InternalTestUtils.kt$import net.corda.nodeapi.internal.loadDevCaTrustStore</ID>
|
||||
<ID>UnusedImports:NetworkMap.kt$import net.corda.core.node.NodeInfo</ID>
|
||||
<ID>UnusedImports:NodeParameters.kt$import net.corda.core.identity.Party</ID>
|
||||
<ID>UnusedImports:SerializerFactory.kt$import java.io.NotSerializableException</ID>
|
||||
@ -1960,7 +1946,6 @@
|
||||
<ID>WildcardImport:CordaExceptionTest.kt$import org.junit.Assert.*</ID>
|
||||
<ID>WildcardImport:CordaFutureImplTest.kt$import com.nhaarman.mockito_kotlin.*</ID>
|
||||
<ID>WildcardImport:CordaInternal.kt$import kotlin.annotation.AnnotationTarget.*</ID>
|
||||
<ID>WildcardImport:CordaMigration.kt$import net.corda.node.services.persistence.*</ID>
|
||||
<ID>WildcardImport:CordaModule.kt$import com.fasterxml.jackson.annotation.*</ID>
|
||||
<ID>WildcardImport:CordaModule.kt$import com.fasterxml.jackson.databind.*</ID>
|
||||
<ID>WildcardImport:CordaModule.kt$import net.corda.core.contracts.*</ID>
|
||||
@ -1990,7 +1975,6 @@
|
||||
<ID>WildcardImport:DBTransactionStorage.kt$import javax.persistence.*</ID>
|
||||
<ID>WildcardImport:DBTransactionStorage.kt$import net.corda.core.serialization.*</ID>
|
||||
<ID>WildcardImport:DBTransactionStorage.kt$import net.corda.nodeapi.internal.persistence.*</ID>
|
||||
<ID>WildcardImport:DBTransactionStorageTests.kt$import net.corda.testing.core.*</ID>
|
||||
<ID>WildcardImport:DeleteForDJVM.kt$import kotlin.annotation.AnnotationTarget.*</ID>
|
||||
<ID>WildcardImport:DemoBench.kt$import tornadofx.*</ID>
|
||||
<ID>WildcardImport:DemoBenchNodeInfoFilesCopier.kt$import tornadofx.*</ID>
|
||||
@ -2008,7 +1992,6 @@
|
||||
<ID>WildcardImport:DigitalSignatureWithCert.kt$import java.security.cert.*</ID>
|
||||
<ID>WildcardImport:DistributedServiceTests.kt$import net.corda.testing.core.*</ID>
|
||||
<ID>WildcardImport:DockerInstantiator.kt$import com.github.dockerjava.api.model.*</ID>
|
||||
<ID>WildcardImport:DriverDSLImpl.kt$import net.corda.testing.driver.*</ID>
|
||||
<ID>WildcardImport:DummyContract.kt$import net.corda.core.contracts.*</ID>
|
||||
<ID>WildcardImport:DummyContractV2.kt$import net.corda.core.contracts.*</ID>
|
||||
<ID>WildcardImport:DummyContractV3.kt$import net.corda.core.contracts.*</ID>
|
||||
@ -2036,12 +2019,10 @@
|
||||
<ID>WildcardImport:FlowCheckpointCordapp.kt$import net.corda.core.flows.*</ID>
|
||||
<ID>WildcardImport:FlowCheckpointVersionNodeStartupCheckTest.kt$import net.corda.core.flows.*</ID>
|
||||
<ID>WildcardImport:FlowCheckpointVersionNodeStartupCheckTest.kt$import net.corda.core.internal.*</ID>
|
||||
<ID>WildcardImport:FlowFrameworkPersistenceTests.kt$import net.corda.testing.node.internal.*</ID>
|
||||
<ID>WildcardImport:FlowFrameworkTripartyTests.kt$import net.corda.testing.node.internal.*</ID>
|
||||
<ID>WildcardImport:FlowLogicRefFactoryImpl.kt$import net.corda.core.flows.*</ID>
|
||||
<ID>WildcardImport:FlowMatchers.kt$import net.corda.coretesting.internal.matchers.*</ID>
|
||||
<ID>WildcardImport:FlowOverrideTests.kt$import net.corda.core.flows.*</ID>
|
||||
<ID>WildcardImport:FlowRetryTest.kt$import net.corda.core.flows.*</ID>
|
||||
<ID>WildcardImport:FlowStackSnapshotTest.kt$import net.corda.core.flows.*</ID>
|
||||
<ID>WildcardImport:FlowStateMachine.kt$import net.corda.core.flows.*</ID>
|
||||
<ID>WildcardImport:FlowsDrainingModeContentionTest.kt$import net.corda.core.flows.*</ID>
|
||||
@ -2087,7 +2068,6 @@
|
||||
<ID>WildcardImport:InternalMockNetwork.kt$import net.corda.core.internal.*</ID>
|
||||
<ID>WildcardImport:InternalMockNetwork.kt$import net.corda.node.services.config.*</ID>
|
||||
<ID>WildcardImport:InternalMockNetwork.kt$import net.corda.testing.node.*</ID>
|
||||
<ID>WildcardImport:InternalTestUtils.kt$import net.corda.core.contracts.*</ID>
|
||||
<ID>WildcardImport:IssuerModel.kt$import tornadofx.*</ID>
|
||||
<ID>WildcardImport:JVMConfig.kt$import tornadofx.*</ID>
|
||||
<ID>WildcardImport:JacksonSupport.kt$import com.fasterxml.jackson.core.*</ID>
|
||||
@ -2187,7 +2167,6 @@
|
||||
<ID>WildcardImport:NodeAttachmentServiceTest.kt$import org.assertj.core.api.Assertions.*</ID>
|
||||
<ID>WildcardImport:NodeAttachmentTrustCalculator.kt$import net.corda.core.internal.*</ID>
|
||||
<ID>WildcardImport:NodeBasedTest.kt$import net.corda.node.services.config.*</ID>
|
||||
<ID>WildcardImport:NodeController.kt$import net.corda.core.internal.*</ID>
|
||||
<ID>WildcardImport:NodeController.kt$import tornadofx.*</ID>
|
||||
<ID>WildcardImport:NodeControllerTest.kt$import kotlin.test.*</ID>
|
||||
<ID>WildcardImport:NodeData.kt$import tornadofx.*</ID>
|
||||
@ -2197,7 +2176,6 @@
|
||||
<ID>WildcardImport:NodeInterestRates.kt$import net.corda.core.flows.*</ID>
|
||||
<ID>WildcardImport:NodeInterestRatesTest.kt$import net.corda.testing.core.*</ID>
|
||||
<ID>WildcardImport:NodeInterestRatesTest.kt$import org.junit.Assert.*</ID>
|
||||
<ID>WildcardImport:NodeProcess.kt$import net.corda.core.internal.*</ID>
|
||||
<ID>WildcardImport:NodeRegistrationTest.kt$import javax.ws.rs.*</ID>
|
||||
<ID>WildcardImport:NodeSchedulerService.kt$import net.corda.core.internal.*</ID>
|
||||
<ID>WildcardImport:NodeSchedulerServiceTest.kt$import com.nhaarman.mockito_kotlin.*</ID>
|
||||
|
@ -129,7 +129,7 @@ class ArtemisRpcTests {
|
||||
|
||||
private fun <OPS : RPCOps> InternalRPCMessagingClient.start(ops: OPS, securityManager: RPCSecurityManager, brokerControl: ActiveMQServerControl) {
|
||||
apply {
|
||||
init(ops, securityManager, TestingNamedCacheFactory())
|
||||
init(listOf(ops), securityManager, TestingNamedCacheFactory())
|
||||
start(brokerControl)
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import net.corda.core.internal.div
|
||||
import net.corda.core.internal.inputStream
|
||||
import net.corda.core.internal.isRegularFile
|
||||
import net.corda.core.internal.list
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import net.corda.core.internal.readFully
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
@ -21,6 +20,7 @@ import net.corda.node.services.statemachine.CountUpDownLatch
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.driver.internal.checkpoint.CheckpointRpcHelper.checkpointsRpc
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.enclosedCordapp
|
||||
import org.junit.Test
|
||||
@ -44,15 +44,14 @@ class DumpCheckpointsTest {
|
||||
val nodeAHandle = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||
|
||||
CordaRPCClient(nodeAHandle.rpcAddress).start(user.username, user.password).use {
|
||||
val proxy = it.proxy as InternalCordaRPCOps
|
||||
|
||||
// 1 for GetNumberOfCheckpointsFlow itself
|
||||
val checkPointCountFuture = proxy.startFlow(::GetNumberOfCheckpointsFlow).returnValue
|
||||
val checkPointCountFuture = it.proxy.startFlow(::GetNumberOfCheckpointsFlow).returnValue
|
||||
|
||||
val logDirPath = nodeAHandle.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME
|
||||
logDirPath.createDirectories()
|
||||
dumpCheckPointLatch.await()
|
||||
proxy.dumpCheckpoints()
|
||||
nodeAHandle.checkpointsRpc.use { checkpointRPCOps -> checkpointRPCOps.dumpCheckpoints() }
|
||||
|
||||
flowProceedLatch.countDown()
|
||||
assertEquals(1, checkPointCountFuture.get())
|
||||
|
@ -37,7 +37,8 @@ import net.corda.core.internal.concurrent.flatMap
|
||||
import net.corda.core.internal.concurrent.map
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import net.corda.core.internal.messaging.AttachmentTrustInfoRPCOps
|
||||
import net.corda.core.internal.messaging.FlowManagerRPCOps
|
||||
import net.corda.core.internal.notary.NotaryService
|
||||
import net.corda.core.internal.rootMessage
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
@ -71,6 +72,8 @@ import net.corda.djvm.source.EmptyApi
|
||||
import net.corda.djvm.source.UserSource
|
||||
import net.corda.node.CordaClock
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.attachments.AttachmentTrustInfoRPCOpsImpl
|
||||
import net.corda.node.internal.checkpoints.FlowManagerRPCOpsImpl
|
||||
import net.corda.node.internal.classloading.requireAnnotation
|
||||
import net.corda.node.internal.cordapp.CordappConfigFileProvider
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
@ -394,21 +397,28 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
return this
|
||||
}
|
||||
|
||||
/** The implementation of the [CordaRPCOps] interface used by this node. */
|
||||
open fun makeRPCOps(cordappLoader: CordappLoader, checkpointDumper: CheckpointDumperImpl): CordaRPCOps {
|
||||
val ops: InternalCordaRPCOps = CordaRPCOpsImpl(
|
||||
/** The implementation of the [RPCOps] interfaces used by this node. */
|
||||
open fun makeRPCOps(cordappLoader: CordappLoader): List<RPCOps> {
|
||||
val cordaRPCOpsImpl = Pair(CordaRPCOps::class.java, CordaRPCOpsImpl(
|
||||
services,
|
||||
smm,
|
||||
flowStarter,
|
||||
checkpointDumper
|
||||
flowStarter
|
||||
) {
|
||||
shutdownExecutor.submit(::stop)
|
||||
}.also { it.closeOnStop() }
|
||||
val proxies = mutableListOf<(InternalCordaRPCOps) -> InternalCordaRPCOps>()
|
||||
// Mind that order is relevant here.
|
||||
proxies += ::AuthenticatedRpcOpsProxy
|
||||
proxies += { ThreadContextAdjustingRpcOpsProxy(it, cordappLoader.appClassLoader) }
|
||||
return proxies.fold(ops) { delegate, decorate -> decorate(delegate) }
|
||||
}.also { it.closeOnStop() })
|
||||
|
||||
val checkpointRPCOpsImpl = Pair(FlowManagerRPCOps::class.java, FlowManagerRPCOpsImpl(checkpointDumper))
|
||||
|
||||
val attachmentTrustInfoRPCOps = Pair(AttachmentTrustInfoRPCOps::class.java, AttachmentTrustInfoRPCOpsImpl(services.attachmentTrustCalculator))
|
||||
|
||||
return listOf(cordaRPCOpsImpl, checkpointRPCOpsImpl, attachmentTrustInfoRPCOps).map { rpcOpsImplPair ->
|
||||
// Mind that order of proxies is important
|
||||
val targetInterface = rpcOpsImplPair.first
|
||||
val stage1Proxy = AuthenticatedRpcOpsProxy.proxy(rpcOpsImplPair.second, targetInterface)
|
||||
val stage2Proxy = ThreadContextAdjustingRpcOpsProxy.proxy(stage1Proxy, targetInterface, cordappLoader.appClassLoader)
|
||||
|
||||
stage2Proxy
|
||||
}
|
||||
}
|
||||
|
||||
private fun quasarExcludePackages(nodeConfiguration: NodeConfiguration) {
|
||||
@ -513,7 +523,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
installCoreFlows()
|
||||
registerCordappFlows()
|
||||
services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows }
|
||||
val rpcOps = makeRPCOps(cordappLoader, checkpointDumper)
|
||||
val rpcOps = makeRPCOps(cordappLoader)
|
||||
startShell()
|
||||
networkMapClient?.start(trustRoot)
|
||||
|
||||
@ -623,7 +633,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
}
|
||||
|
||||
/** Subclasses must override this to create a "started" node of the desired type, using the provided machinery. */
|
||||
abstract fun createStartedNode(nodeInfo: NodeInfo, rpcOps: CordaRPCOps, notaryService: NotaryService?): S
|
||||
abstract fun createStartedNode(nodeInfo: NodeInfo, rpcOps: List<RPCOps>, notaryService: NotaryService?): S
|
||||
|
||||
private fun verifyCheckpointsCompatible(tokenizableServices: List<Any>) {
|
||||
try {
|
||||
@ -1035,7 +1045,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
|
||||
protected abstract fun makeMessagingService(): MessagingService
|
||||
|
||||
protected abstract fun startMessagingService(rpcOps: RPCOps,
|
||||
protected abstract fun startMessagingService(rpcOps: List<RPCOps>,
|
||||
nodeInfo: NodeInfo,
|
||||
myNotaryIdentity: PartyAndCertificate?,
|
||||
networkParameters: NetworkParameters)
|
||||
|
@ -18,12 +18,11 @@ import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.AttachmentTrustInfo
|
||||
import net.corda.core.internal.FlowStateMachineHandle
|
||||
import net.corda.core.internal.RPC_UPLOADER
|
||||
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.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.messaging.FlowHandle
|
||||
import net.corda.core.messaging.FlowHandleImpl
|
||||
@ -54,7 +53,6 @@ import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.services.api.FlowStarter
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
import net.corda.node.services.rpc.CheckpointDumperImpl
|
||||
import net.corda.node.services.rpc.context
|
||||
import net.corda.node.services.statemachine.StateMachineManager
|
||||
import net.corda.nodeapi.exceptions.NonRpcFlowException
|
||||
@ -75,9 +73,8 @@ internal class CordaRPCOpsImpl(
|
||||
private val services: ServiceHubInternal,
|
||||
private val smm: StateMachineManager,
|
||||
private val flowStarter: FlowStarter,
|
||||
private val checkpointDumper: CheckpointDumperImpl,
|
||||
private val shutdownNode: () -> Unit
|
||||
) : InternalCordaRPCOps, AutoCloseable {
|
||||
) : CordaRPCOps, AutoCloseable {
|
||||
|
||||
private companion object {
|
||||
private val logger = loggerFor<CordaRPCOpsImpl>()
|
||||
@ -157,13 +154,6 @@ internal class CordaRPCOpsImpl(
|
||||
return services.validatedTransactions.track()
|
||||
}
|
||||
|
||||
override fun dumpCheckpoints() = checkpointDumper.dumpCheckpoints()
|
||||
|
||||
override val attachmentTrustInfos: List<AttachmentTrustInfo>
|
||||
get() {
|
||||
return services.attachmentTrustCalculator.calculateAllTrustInfo()
|
||||
}
|
||||
|
||||
override fun stateMachinesSnapshot(): List<StateMachineInfo> {
|
||||
val (snapshot, updates) = stateMachinesFeed()
|
||||
updates.notUsed()
|
||||
|
@ -22,7 +22,6 @@ import net.corda.core.internal.errors.AddressBindingException
|
||||
import net.corda.core.internal.getJavaUpdateVersion
|
||||
import net.corda.core.internal.isRegularFile
|
||||
import net.corda.core.internal.notary.NotaryService
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NodeInfo
|
||||
@ -140,7 +139,7 @@ open class Node(configuration: NodeConfiguration,
|
||||
allowHibernateToManageAppSchema = allowHibernateToManageAppSchema
|
||||
) {
|
||||
|
||||
override fun createStartedNode(nodeInfo: NodeInfo, rpcOps: CordaRPCOps, notaryService: NotaryService?): NodeInfo =
|
||||
override fun createStartedNode(nodeInfo: NodeInfo, rpcOps: List<RPCOps>, notaryService: NotaryService?): NodeInfo =
|
||||
nodeInfo
|
||||
|
||||
companion object {
|
||||
@ -340,7 +339,7 @@ open class Node(configuration: NodeConfiguration,
|
||||
)
|
||||
}
|
||||
|
||||
override fun startMessagingService(rpcOps: RPCOps, nodeInfo: NodeInfo, myNotaryIdentity: PartyAndCertificate?, networkParameters: NetworkParameters) {
|
||||
override fun startMessagingService(rpcOps: List<RPCOps>, nodeInfo: NodeInfo, myNotaryIdentity: PartyAndCertificate?, networkParameters: NetworkParameters) {
|
||||
require(nodeInfo.legalIdentities.size in 1..2) { "Currently nodes must have a primary address and optionally one serviced address" }
|
||||
|
||||
network as P2PMessagingClient
|
||||
|
@ -0,0 +1,16 @@
|
||||
package net.corda.node.internal.attachments
|
||||
|
||||
import net.corda.core.internal.AttachmentTrustCalculator
|
||||
import net.corda.core.internal.AttachmentTrustInfo
|
||||
import net.corda.core.internal.PLATFORM_VERSION
|
||||
import net.corda.core.internal.messaging.AttachmentTrustInfoRPCOps
|
||||
|
||||
class AttachmentTrustInfoRPCOpsImpl(private val attachmentTrustCalculator: AttachmentTrustCalculator) : AttachmentTrustInfoRPCOps {
|
||||
|
||||
override val protocolVersion: Int = PLATFORM_VERSION
|
||||
|
||||
override val attachmentTrustInfos: List<AttachmentTrustInfo>
|
||||
get() {
|
||||
return attachmentTrustCalculator.calculateAllTrustInfo()
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package net.corda.node.internal.checkpoints
|
||||
|
||||
import net.corda.core.internal.PLATFORM_VERSION
|
||||
import net.corda.core.internal.messaging.FlowManagerRPCOps
|
||||
import net.corda.node.services.rpc.CheckpointDumperImpl
|
||||
|
||||
/**
|
||||
* Implementation of [FlowManagerRPCOps]
|
||||
*/
|
||||
internal class FlowManagerRPCOpsImpl(private val checkpointDumper: CheckpointDumperImpl) : FlowManagerRPCOps {
|
||||
|
||||
override val protocolVersion: Int = PLATFORM_VERSION
|
||||
|
||||
override fun dumpCheckpoints() = checkpointDumper.dumpCheckpoints()
|
||||
}
|
@ -1,55 +1,72 @@
|
||||
package net.corda.node.internal.rpc.proxies
|
||||
|
||||
import net.corda.client.rpc.PermissionException
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.internal.utilities.InvocationHandlerTemplate
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.node.internal.rpc.proxies.RpcAuthHelper.methodFullName
|
||||
import net.corda.node.services.rpc.RpcAuthContext
|
||||
import net.corda.node.services.rpc.rpcContext
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Proxy
|
||||
|
||||
/**
|
||||
* Implementation of [CordaRPCOps] that checks authorisation.
|
||||
* Creates proxy that checks entitlements for every RPCOps interface call.
|
||||
*/
|
||||
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
|
||||
* to be present.
|
||||
*
|
||||
* TODO: Why is this logic duplicated vs the actual implementation?
|
||||
*/
|
||||
override val protocolVersion: Int get() = delegate.nodeInfo().platformVersion
|
||||
internal object AuthenticatedRpcOpsProxy {
|
||||
|
||||
// Need overriding to pass additional `listOf(logicType)` argument for polymorphic `startFlow` permissions.
|
||||
override fun <T> startFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?) = guard("startFlowDynamic", listOf(logicType), ::rpcContext) {
|
||||
delegate.startFlowDynamic(logicType, *args)
|
||||
fun <T : RPCOps> proxy(delegate: T, targetInterface: Class<out T>): T {
|
||||
require(targetInterface.isInterface) { "Interface is expected instead of $targetInterface" }
|
||||
val handler = PermissionsEnforcingInvocationHandler(delegate, targetInterface)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return Proxy.newProxyInstance(delegate::class.java.classLoader, arrayOf(targetInterface), handler) as T
|
||||
}
|
||||
|
||||
// Need overriding to pass additional `listOf(logicType)` argument for polymorphic `startFlow` permissions.
|
||||
override fun <T> startTrackedFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?) = guard("startTrackedFlowDynamic", listOf(logicType), ::rpcContext) {
|
||||
delegate.startTrackedFlowDynamic(logicType, *args)
|
||||
}
|
||||
private class PermissionsEnforcingInvocationHandler(override val delegate: Any, private val clazz: Class<*>) : InvocationHandlerTemplate {
|
||||
|
||||
private companion object {
|
||||
private fun proxy(delegate: InternalCordaRPCOps, context: () -> RpcAuthContext): InternalCordaRPCOps {
|
||||
val handler = PermissionsEnforcingInvocationHandler(delegate, context)
|
||||
return Proxy.newProxyInstance(delegate::class.java.classLoader, arrayOf(InternalCordaRPCOps::class.java), handler) as InternalCordaRPCOps
|
||||
private val exemptMethod = RPCOps::class.java.getMethod("getProtocolVersion")
|
||||
|
||||
private val namedInterfaces = setOf(
|
||||
net.corda.core.messaging.CordaRPCOps::class.java)
|
||||
private val namedMethods = setOf("startFlowDynamic", "startTrackedFlowDynamic")
|
||||
|
||||
override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?): Any? {
|
||||
|
||||
if (method == exemptMethod) {
|
||||
// "getProtocolVersion" is an exempt from entitlements check as this is the very first *any* RPCClient calls upon login
|
||||
return super.invoke(proxy, method, arguments)
|
||||
}
|
||||
|
||||
val importantArgs = if (clazz in namedInterfaces && method.name in namedMethods) {
|
||||
// Normally list of arguments makes no difference when checking entitlements, however when starting flows
|
||||
// first argument represents a class name of the flow to be started and special handling applies in this case with
|
||||
// name of the class extracted and passed into `guard` method for entitlements check.
|
||||
val nonNullArgs = requireNotNull(arguments)
|
||||
require(nonNullArgs.isNotEmpty())
|
||||
val firstArgClass = requireNotNull(nonNullArgs[0] as? Class<*>)
|
||||
listOf(firstArgClass)
|
||||
} else emptyList()
|
||||
|
||||
return guard(method, importantArgs, ::rpcContext) { super.invoke(proxy, method, arguments) }
|
||||
}
|
||||
}
|
||||
|
||||
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) }
|
||||
private fun <RESULT> guard(method: Method, args: List<Class<*>>, context: () -> RpcAuthContext, action: () -> RESULT): RESULT {
|
||||
if (!context().isPermitted(methodFullName(method), *(args.map(Class<*>::getName).toTypedArray()))) {
|
||||
throw PermissionException("User not authorized to perform RPC call $method with target $args")
|
||||
} else {
|
||||
return action()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <RESULT> guard(methodName: String, context: () -> RpcAuthContext, action: () -> RESULT) = guard(methodName, emptyList(), context, action)
|
||||
object RpcAuthHelper {
|
||||
const val INTERFACE_SEPARATOR = "#"
|
||||
|
||||
private fun <RESULT> guard(methodName: String, args: List<Class<*>>, context: () -> RpcAuthContext, action: () -> RESULT): RESULT {
|
||||
if (!context().isPermitted(methodName, *(args.map(Class<*>::getName).toTypedArray()))) {
|
||||
throw PermissionException("User not authorized to perform RPC call $methodName with target $args")
|
||||
} else {
|
||||
return action()
|
||||
fun methodFullName(method: Method):String = methodFullName(method.declaringClass, method.name)
|
||||
|
||||
fun methodFullName(clazz: Class<*>, methodName: String): String {
|
||||
require(clazz.isInterface) { "Must be an interface: $clazz"}
|
||||
require(RPCOps::class.java.isAssignableFrom(clazz)) { "Must be assignable from RPCOps: $clazz" }
|
||||
return clazz.name + INTERFACE_SEPARATOR + methodName
|
||||
}
|
||||
}
|
@ -1,30 +1,28 @@
|
||||
package net.corda.node.internal.rpc.proxies
|
||||
|
||||
import net.corda.core.internal.executeWithThreadContextClassLoader
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.internal.utilities.InvocationHandlerTemplate
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Proxy
|
||||
|
||||
/**
|
||||
* A [CordaRPCOps] proxy that adjusts the thread context's class loader temporarily on every invocation with the provided classloader.
|
||||
* A proxy that adjusts the thread context's class loader temporarily on every invocation of supplied interface with the provided classloader.
|
||||
* As an example, this can be used to work-around cases, where 3rd party libraries prioritise the thread context's class loader over the current one,
|
||||
* 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.
|
||||
*/
|
||||
internal class ThreadContextAdjustingRpcOpsProxy(private val delegate: InternalCordaRPCOps, private val classLoader: ClassLoader) : InternalCordaRPCOps by proxy(delegate, classLoader) {
|
||||
private companion object {
|
||||
private fun proxy(delegate: InternalCordaRPCOps, classLoader: ClassLoader): InternalCordaRPCOps {
|
||||
val handler = ThreadContextAdjustingRpcOpsProxy.ThreadContextAdjustingInvocationHandler(delegate, classLoader)
|
||||
return Proxy.newProxyInstance(delegate::class.java.classLoader, arrayOf(InternalCordaRPCOps::class.java), handler) as InternalCordaRPCOps
|
||||
}
|
||||
internal object ThreadContextAdjustingRpcOpsProxy {
|
||||
fun <T : RPCOps> proxy(delegate: T, clazz: Class<out T>, classLoader: ClassLoader): T {
|
||||
require(clazz.isInterface) { "Interface is expected instead of $clazz" }
|
||||
val handler = ThreadContextAdjustingInvocationHandler(delegate, classLoader)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return Proxy.newProxyInstance(delegate::class.java.classLoader, arrayOf(clazz), handler) as T
|
||||
}
|
||||
|
||||
private class ThreadContextAdjustingInvocationHandler(override val delegate: InternalCordaRPCOps, private val classLoader: ClassLoader) : InvocationHandlerTemplate {
|
||||
internal class ThreadContextAdjustingInvocationHandler(override val delegate: Any, private val classLoader: ClassLoader) : InvocationHandlerTemplate {
|
||||
override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?): Any? {
|
||||
return executeWithThreadContextClassLoader(this.classLoader) { super.invoke(proxy, method, arguments) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
package net.corda.node.internal.security
|
||||
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache
|
||||
import net.corda.core.internal.toMultiMap
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.node.internal.rpc.proxies.RpcAuthHelper.INTERFACE_SEPARATOR
|
||||
import net.corda.node.internal.rpc.proxies.RpcAuthHelper.methodFullName
|
||||
import org.apache.shiro.authz.Permission
|
||||
import org.apache.shiro.authz.permission.PermissionResolver
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.lang.reflect.Method
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaMethod
|
||||
|
||||
/*
|
||||
* A [org.apache.shiro.authz.permission.PermissionResolver] implementation for RPC permissions.
|
||||
* Provides a method to construct an [RPCPermission] instance from its string representation
|
||||
* in the form used by a Node admin.
|
||||
*
|
||||
* Currently valid permission strings have the forms:
|
||||
*
|
||||
* - `ALL`: allowing all type of RPC calls
|
||||
*
|
||||
* - `InvokeRpc.$RPCMethodName`: allowing to call a given RPC method without restrictions on its arguments.
|
||||
*
|
||||
* - `StartFlow.$FlowClassName`: allowing to call a `startFlow*` RPC method targeting a Flow instance
|
||||
* of a given class
|
||||
*/
|
||||
internal object RPCPermissionResolver : PermissionResolver {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(RPCPermissionResolver::class.java)
|
||||
|
||||
private const val SEPARATOR = '.'
|
||||
private const val NEW_STYLE_SEP = ":"
|
||||
private const val ACTION_START_FLOW = "startflow"
|
||||
private const val ACTION_INVOKE_RPC = "invokerpc"
|
||||
private const val ACTION_ALL = "all"
|
||||
private val FLOW_RPC_CALLS = setOf(
|
||||
"startFlowDynamic",
|
||||
"startTrackedFlowDynamic",
|
||||
"startFlow",
|
||||
"startTrackedFlow")
|
||||
|
||||
override fun resolvePermission(representation: String): Permission {
|
||||
when (representation.substringBefore(SEPARATOR).toLowerCase()) {
|
||||
ACTION_INVOKE_RPC -> {
|
||||
val rpcCall = representation.substringAfter(SEPARATOR, "")
|
||||
require(representation.count { it == SEPARATOR } == 1 && rpcCall.isNotEmpty()) {
|
||||
"Malformed permission string"
|
||||
}
|
||||
val legacyPermitted = when (rpcCall) {
|
||||
"startFlow" -> setOf("startFlowDynamic", rpcCall)
|
||||
"startTrackedFlow" -> setOf("startTrackedFlowDynamic", rpcCall)
|
||||
else -> setOf(rpcCall)
|
||||
}
|
||||
return RPCPermission(legacyPermitted.toFullyQualified())
|
||||
}
|
||||
ACTION_START_FLOW -> {
|
||||
val targetFlow = representation.substringAfter(SEPARATOR, "")
|
||||
require(targetFlow.isNotEmpty()) {
|
||||
"Missing target flow after StartFlow"
|
||||
}
|
||||
return RPCPermission(FLOW_RPC_CALLS.toFullyQualified(), targetFlow)
|
||||
}
|
||||
ACTION_ALL -> {
|
||||
// Leaving empty set of targets and actions to match everything
|
||||
return RPCPermission()
|
||||
}
|
||||
else -> return attemptNewStyleParsing(representation)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Set<String>.toFullyQualified(): Set<String> {
|
||||
return map { methodFullName(CordaRPCOps::class.java, it) }.toSet()
|
||||
}
|
||||
|
||||
/**
|
||||
* New style permissions representation:
|
||||
* 1. Fully qualified form: InvokeRpc:com.fully.qualified.package.CustomClientRpcOps#firstMethod
|
||||
* 2. All methods of the interface: InvokeRpc:com.fully.qualified.package.CustomClientRpcOps#ALL
|
||||
* 3. Methods of specific group: InvokeRpc:com.fully.qualified.package.CustomClientRpcOps#READONLY
|
||||
*/
|
||||
private fun attemptNewStyleParsing(permAsString: String): Permission {
|
||||
return when(permAsString.substringBefore(NEW_STYLE_SEP).toLowerCase()) {
|
||||
ACTION_INVOKE_RPC -> {
|
||||
val interfaceAndMethods = permAsString.substringAfter(NEW_STYLE_SEP, "")
|
||||
val interfaceParts = interfaceAndMethods.split(INTERFACE_SEPARATOR)
|
||||
require(interfaceParts.size == 2) { "Malformed to comply with new style of InvokeRpc: $interfaceAndMethods" }
|
||||
val methodsMap = requireNotNull(cache.get(interfaceParts[0]))
|
||||
{ "Method map for ${interfaceParts[0]} must not be null in the cache. There must have been error processing interface. " +
|
||||
"Please look at the error log lines above." }
|
||||
val lookupKey = interfaceAndMethods.toLowerCase()
|
||||
val methods = requireNotNull(methodsMap[lookupKey]) { "Cannot find record for " +
|
||||
"'$lookupKey' for interface '${interfaceParts[0]}' in $methodsMap. " +
|
||||
"Please check permissions configuration string '$permAsString' matching class representation." }
|
||||
RPCPermission(methods)
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unable to parse permission as string: $permAsString")
|
||||
}
|
||||
}
|
||||
|
||||
private val cache: LoadingCache<String, Map<String, Set<String>>> = Caffeine.newBuilder()
|
||||
.maximumSize(java.lang.Long.getLong("net.corda.node.internal.security.rpc.interface.cacheSize", 20))
|
||||
.build(InterfaceMethodMapCacheLoader())
|
||||
|
||||
private class InterfaceMethodMapCacheLoader : CacheLoader<String, Map<String, Set<String>>> {
|
||||
override fun load(interfaceName: String): Map<String, Set<String>>? {
|
||||
return try {
|
||||
inspectInterface(interfaceName)
|
||||
} catch (ex: Exception) {
|
||||
logger.error("Unexpected error when populating cache for $interfaceName", ex)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map where key is either:
|
||||
* - fully qualified interface method name;
|
||||
* or
|
||||
* - Wildcard string representing the group of methods like: ALL, READ_ONLY, etc.
|
||||
* Value is always a set of fully qualified method names.
|
||||
*/
|
||||
internal fun inspectInterface(interfaceName: String): Map<String, Set<String>> {
|
||||
val interfaceClass = Class.forName(interfaceName).kotlin
|
||||
require(interfaceClass.java.isInterface) { "Must be an interface: $interfaceClass"}
|
||||
require(RPCOps::class.java.isAssignableFrom(interfaceClass.java)) { "Must be assignable from RPCOps: $interfaceClass" }
|
||||
|
||||
val membersPairs = interfaceClass.members.flatMap { member ->
|
||||
when(member) {
|
||||
is KFunction -> {
|
||||
val method = member.javaMethod
|
||||
if (method != null) {
|
||||
processMethod(method, interfaceClass)
|
||||
} else {
|
||||
logger.info("KFunction $member does not have Java representation - ignoring")
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
is KProperty -> {
|
||||
val method = member.getter.javaMethod
|
||||
if (method != null) {
|
||||
processMethod(method, interfaceClass)
|
||||
} else {
|
||||
logger.info("KProperty $member does not have Java representation - ignoring")
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
logger.info("$member is an unhandled type of KCallable - ignoring")
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
// Pack the pairs into desired resulting data structure
|
||||
return membersPairs.toMultiMap().mapValues { it.value.toSet() }
|
||||
}
|
||||
|
||||
private fun processMethod(method: Method, interfaceClass: KClass<out Any>): List<Pair<String, String>> {
|
||||
if(!RPCOps::class.java.isAssignableFrom(method.declaringClass)) {
|
||||
// To prevent going too deep to Object level
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val allKey = methodFullName(interfaceClass.java, ACTION_ALL).toLowerCase()
|
||||
val methodFullName = methodFullName(method)
|
||||
return listOf(allKey to methodFullName) + // ALL group
|
||||
listOf(methodFullName.toLowerCase() to methodFullName) // Full method names individually
|
||||
}
|
||||
}
|
@ -16,10 +16,8 @@ import org.apache.shiro.authc.*
|
||||
import org.apache.shiro.authc.credential.PasswordMatcher
|
||||
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher
|
||||
import org.apache.shiro.authz.AuthorizationInfo
|
||||
import org.apache.shiro.authz.Permission
|
||||
import org.apache.shiro.authz.SimpleAuthorizationInfo
|
||||
import org.apache.shiro.authz.permission.DomainPermission
|
||||
import org.apache.shiro.authz.permission.PermissionResolver
|
||||
import org.apache.shiro.cache.CacheManager
|
||||
import org.apache.shiro.mgt.DefaultSecurityManager
|
||||
import org.apache.shiro.realm.AuthorizingRealm
|
||||
@ -106,7 +104,7 @@ class RPCSecurityManagerImpl(config: AuthServiceConfig, cacheFactory: NamedCache
|
||||
* represented by instances of the [Permission] interface which offers a single method: [implies], to
|
||||
* test if the 'x implies y' binary predicate is satisfied.
|
||||
*/
|
||||
private class RPCPermission : DomainPermission {
|
||||
internal class RPCPermission : DomainPermission {
|
||||
|
||||
/**
|
||||
* Helper constructor directly setting actions and target field
|
||||
@ -123,63 +121,6 @@ private class RPCPermission : DomainPermission {
|
||||
constructor() : super()
|
||||
}
|
||||
|
||||
/*
|
||||
* A [org.apache.shiro.authz.permission.PermissionResolver] implementation for RPC permissions.
|
||||
* Provides a method to construct an [RPCPermission] instance from its string representation
|
||||
* in the form used by a Node admin.
|
||||
*
|
||||
* Currently valid permission strings have the forms:
|
||||
*
|
||||
* - `ALL`: allowing all type of RPC calls
|
||||
*
|
||||
* - `InvokeRpc.$RPCMethodName`: allowing to call a given RPC method without restrictions on its arguments.
|
||||
*
|
||||
* - `StartFlow.$FlowClassName`: allowing to call a `startFlow*` RPC method targeting a Flow instance
|
||||
* of a given class
|
||||
*/
|
||||
private object RPCPermissionResolver : PermissionResolver {
|
||||
|
||||
private const val SEPARATOR = '.'
|
||||
private const val ACTION_START_FLOW = "startflow"
|
||||
private const val ACTION_INVOKE_RPC = "invokerpc"
|
||||
private const val ACTION_ALL = "all"
|
||||
private val FLOW_RPC_CALLS = setOf(
|
||||
"startFlowDynamic",
|
||||
"startTrackedFlowDynamic",
|
||||
"startFlow",
|
||||
"startTrackedFlow")
|
||||
|
||||
override fun resolvePermission(representation: String): Permission {
|
||||
val action = representation.substringBefore(SEPARATOR).toLowerCase()
|
||||
when (action) {
|
||||
ACTION_INVOKE_RPC -> {
|
||||
val rpcCall = representation.substringAfter(SEPARATOR, "")
|
||||
require(representation.count { it == SEPARATOR } == 1 && !rpcCall.isEmpty()) {
|
||||
"Malformed permission string"
|
||||
}
|
||||
val permitted = when(rpcCall) {
|
||||
"startFlow" -> setOf("startFlowDynamic", rpcCall)
|
||||
"startTrackedFlow" -> setOf("startTrackedFlowDynamic", rpcCall)
|
||||
else -> setOf(rpcCall)
|
||||
}
|
||||
return RPCPermission(permitted)
|
||||
}
|
||||
ACTION_START_FLOW -> {
|
||||
val targetFlow = representation.substringAfter(SEPARATOR, "")
|
||||
require(targetFlow.isNotEmpty()) {
|
||||
"Missing target flow after StartFlow"
|
||||
}
|
||||
return RPCPermission(FLOW_RPC_CALLS, targetFlow)
|
||||
}
|
||||
ACTION_ALL -> {
|
||||
// Leaving empty set of targets and actions to match everything
|
||||
return RPCPermission()
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unknown permission action specifier: $action")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ShiroAuthorizingSubject(
|
||||
private val subjectId: PrincipalCollection,
|
||||
private val manager: DefaultSecurityManager) : AuthorizingSubject {
|
||||
|
@ -21,7 +21,7 @@ class InternalRPCMessagingClient(val sslConfig: MutualSslConfiguration, val serv
|
||||
private var locator: ServerLocator? = null
|
||||
private var rpcServer: RPCServer? = null
|
||||
|
||||
fun init(rpcOps: RPCOps, securityManager: RPCSecurityManager, cacheFactory: NamedCacheFactory) = synchronized(this) {
|
||||
fun init(rpcOps: List<RPCOps>, securityManager: RPCSecurityManager, cacheFactory: NamedCacheFactory) = synchronized(this) {
|
||||
|
||||
val tcpTransport = ArtemisTcpTransport.rpcInternalClientTcpTransport(serverAddress, sslConfig)
|
||||
locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply {
|
||||
|
@ -14,7 +14,6 @@ import net.corda.core.context.Trace.InvocationId
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.LifeCycle
|
||||
import net.corda.core.internal.NamedCacheFactory
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
@ -177,10 +176,10 @@ class RPCServer(
|
||||
val groupedMethods = with(interfaceClass) {
|
||||
/*
|
||||
* Until version 4.3, rpc calls did not include class names.
|
||||
* Up to this version, only CordaRPCOps and InternalCordaRPCOps were supported.
|
||||
* Up to this version, only CordaRPCOps was supported.
|
||||
* So, for these classes methods are registered without their class name as well to preserve backwards compatibility.
|
||||
*/
|
||||
if(interfaceClass == CordaRPCOps::class.java || interfaceClass == InternalCordaRPCOps::class.java) {
|
||||
if (interfaceClass == CordaRPCOps::class.java) {
|
||||
methods.groupBy { it.name }
|
||||
} else {
|
||||
methods.groupBy { interfaceClass.name + CLASS_METHOD_DIVIDER + it.name }
|
||||
|
@ -98,7 +98,7 @@ class CordaRPCOpsImplTest {
|
||||
fun setup() {
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = FINANCE_CORDAPPS)
|
||||
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
|
||||
rpc = aliceNode.rpcOps
|
||||
rpc = aliceNode.cordaRPCOps
|
||||
CURRENT_RPC_CONTEXT.set(RpcAuthContext(InvocationContext.rpc(testActor()), buildSubject("TEST_USER", emptySet())))
|
||||
|
||||
mockNet.runNetwork()
|
||||
|
@ -3,7 +3,7 @@ package net.corda.node.internal.rpc.proxies
|
||||
import com.nhaarman.mockito_kotlin.any
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito.`when`
|
||||
@ -12,9 +12,9 @@ class ThreadContextAdjustingRpcOpsProxyTest {
|
||||
|
||||
private val coreOps = mock<InstrumentedCordaRPCOps>()
|
||||
private val mockClassloader = mock<ClassLoader>()
|
||||
private val proxy = ThreadContextAdjustingRpcOpsProxy(coreOps, mockClassloader)
|
||||
private val proxy = ThreadContextAdjustingRpcOpsProxy.proxy(coreOps, CordaRPCOps::class.java, mockClassloader)
|
||||
|
||||
private interface InstrumentedCordaRPCOps : InternalCordaRPCOps {
|
||||
private interface InstrumentedCordaRPCOps : CordaRPCOps {
|
||||
fun getThreadContextClassLoader(): ClassLoader = Thread.currentThread().contextClassLoader
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,57 @@
|
||||
package net.corda.node.internal.security
|
||||
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.node.internal.rpc.proxies.RpcAuthHelper.methodFullName
|
||||
import org.junit.Test
|
||||
|
||||
import java.time.ZonedDateTime
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class RPCPermissionResolverTest {
|
||||
|
||||
@Suppress("unused")
|
||||
interface Alpha : RPCOps {
|
||||
fun readAlpha() : String
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
interface Beta : Alpha {
|
||||
val betaValue : Int
|
||||
|
||||
fun writeBeta(foo: Int)
|
||||
|
||||
fun nothingSpecial() : Int
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
interface Gamma : Beta {
|
||||
fun readGamma() : ZonedDateTime
|
||||
}
|
||||
|
||||
private val readAlphaMethod = methodFullName(Alpha::class.java.getMethod("readAlpha"))
|
||||
private val readAlphaMethodKey = readAlphaMethod.toLowerCase()
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `test Alpha`() {
|
||||
with(RPCPermissionResolver.inspectInterface(Alpha::class.java.name)) {
|
||||
assertEquals(3, size, toString()) // protocolVersion, ALL, readAlpha
|
||||
assertEquals(setOf(readAlphaMethod), this[readAlphaMethodKey], toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `test Beta`() {
|
||||
with(RPCPermissionResolver.inspectInterface(Beta::class.java.name)) {
|
||||
assertEquals(6, size, toString()) // protocolVersion, ALL, readAlpha
|
||||
// and 3 x Beta methods
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `test Gamma`() {
|
||||
with(RPCPermissionResolver.inspectInterface(Gamma::class.java.name)) {
|
||||
assertEquals(7, size, toString()) // protocolVersion, ALL, readAlpha,
|
||||
// 3 x Beta methods and 1 Gamma method
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package net.corda.node.services
|
||||
import net.corda.core.context.AuthServiceId
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.node.internal.rpc.proxies.RpcAuthHelper
|
||||
import net.corda.node.internal.security.Password
|
||||
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
||||
import net.corda.node.internal.security.tryAuthenticate
|
||||
@ -152,14 +153,14 @@ class RPCSecurityManagerTest {
|
||||
userRealms.tryAuthenticate("user", Password("password"))!!,
|
||||
userRealms.buildSubject("user"))) {
|
||||
for (request in permitted) {
|
||||
val call = request.first()
|
||||
val methodName = RpcAuthHelper.methodFullName(CordaRPCOps::class.java, request.first())
|
||||
val args = request.drop(1).toTypedArray()
|
||||
require(subject.isPermitted(request.first(), *args)) {
|
||||
"User ${subject.principal} should be permitted $call with target '${request.toList()}'"
|
||||
require(subject.isPermitted(methodName, *args)) {
|
||||
"User ${subject.principal} should be permitted $methodName with target '${request.toList()}'"
|
||||
}
|
||||
if (args.isEmpty()) {
|
||||
require(subject.isPermitted(request.first(), "XXX")) {
|
||||
"User ${subject.principal} should be permitted $call with any target"
|
||||
require(subject.isPermitted(methodName, "XXX")) {
|
||||
"User ${subject.principal} should be permitted $methodName with any target"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
package net.corda.testing.driver.internal.checkpoint
|
||||
|
||||
import net.corda.core.internal.messaging.FlowManagerRPCOps
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
|
||||
object CheckpointRpcHelper {
|
||||
|
||||
interface CloseableFlowManagerRPCOps : FlowManagerRPCOps, AutoCloseable
|
||||
|
||||
val NodeHandle.checkpointsRpc: CloseableFlowManagerRPCOps
|
||||
get() {
|
||||
val user = rpcUsers.first()
|
||||
|
||||
val rpcConnection = net.corda.client.rpc.internal.RPCClient<FlowManagerRPCOps>(rpcAddress)
|
||||
.start(FlowManagerRPCOps::class.java, user.username, user.password)
|
||||
val proxy = rpcConnection.proxy
|
||||
|
||||
return object : CloseableFlowManagerRPCOps, FlowManagerRPCOps by proxy {
|
||||
override fun close() {
|
||||
rpcConnection.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -109,7 +109,7 @@ interface TestStartedNode {
|
||||
val services: StartedNodeServices
|
||||
val smm: StateMachineManager
|
||||
val attachments: NodeAttachmentService
|
||||
val rpcOps: CordaRPCOps
|
||||
val rpcOpsList: List<RPCOps>
|
||||
val network: MockNodeMessagingService
|
||||
val database: CordaPersistence
|
||||
val notaryService: NotaryService?
|
||||
@ -135,6 +135,9 @@ interface TestStartedNode {
|
||||
fun <T : FlowLogic<*>> registerInitiatedFlow(initiatedFlowClass: Class<T>, track: Boolean = false): Observable<T>
|
||||
|
||||
fun <T : FlowLogic<*>> registerInitiatedFlow(initiatingFlowClass: Class<out FlowLogic<*>>, initiatedFlowClass: Class<T>, track: Boolean = false): Observable<T>
|
||||
|
||||
val cordaRPCOps: CordaRPCOps
|
||||
get() = rpcOpsList.mapNotNull { it as? CordaRPCOps }.single()
|
||||
}
|
||||
|
||||
open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
|
||||
@ -306,7 +309,7 @@ open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
|
||||
override val info: NodeInfo,
|
||||
override val smm: StateMachineManager,
|
||||
override val database: CordaPersistence,
|
||||
override val rpcOps: CordaRPCOps,
|
||||
override val rpcOpsList: List<RPCOps>,
|
||||
override val notaryService: NotaryService?) : TestStartedNode {
|
||||
|
||||
override fun dispose() = internals.stop()
|
||||
@ -347,7 +350,7 @@ open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
|
||||
|
||||
override val started: TestStartedNode? get() = super.started
|
||||
|
||||
override fun createStartedNode(nodeInfo: NodeInfo, rpcOps: CordaRPCOps, notaryService: NotaryService?): TestStartedNode {
|
||||
override fun createStartedNode(nodeInfo: NodeInfo, rpcOps: List<RPCOps>, notaryService: NotaryService?): TestStartedNode {
|
||||
return TestStartedNodeImpl(
|
||||
this,
|
||||
attachments,
|
||||
@ -380,7 +383,7 @@ open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
|
||||
return MockNodeMessagingService(configuration, serverThread).closeOnStop()
|
||||
}
|
||||
|
||||
override fun startMessagingService(rpcOps: RPCOps,
|
||||
override fun startMessagingService(rpcOps: List<RPCOps>,
|
||||
nodeInfo: NodeInfo,
|
||||
myNotaryIdentity: PartyAndCertificate?,
|
||||
networkParameters: NetworkParameters) {
|
||||
|
@ -21,7 +21,6 @@ import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.inputStream
|
||||
import net.corda.core.internal.list
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import net.corda.core.messaging.ClientRpcSslOptions
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.startFlow
|
||||
@ -49,6 +48,7 @@ import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.driver.internal.NodeHandleInternal
|
||||
import net.corda.testing.driver.internal.checkpoint.CheckpointRpcHelper.checkpointsRpc
|
||||
import net.corda.testing.internal.useSslRpcOverrides
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.enclosedCordapp
|
||||
@ -299,7 +299,7 @@ class InteractiveShellIntegrationTest {
|
||||
(alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).createDirectories()
|
||||
alice.rpc.startFlow(::ExternalOperationFlow)
|
||||
ExternalOperation.lock.acquire()
|
||||
InteractiveShell.runDumpCheckpoints(alice.rpc as InternalCordaRPCOps)
|
||||
alice.checkpointsRpc.use { InteractiveShell.runDumpCheckpoints(it) }
|
||||
ExternalOperation.lock2.release()
|
||||
|
||||
val zipFile = (alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list().first { "checkpoints_dump-" in it.toString() }
|
||||
@ -322,7 +322,7 @@ class InteractiveShellIntegrationTest {
|
||||
(alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).createDirectories()
|
||||
alice.rpc.startFlow(::ExternalAsyncOperationFlow)
|
||||
ExternalAsyncOperation.lock.acquire()
|
||||
InteractiveShell.runDumpCheckpoints(alice.rpc as InternalCordaRPCOps)
|
||||
alice.checkpointsRpc.use { InteractiveShell.runDumpCheckpoints(it) }
|
||||
ExternalAsyncOperation.future.complete(null)
|
||||
val zipFile = (alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list().first { "checkpoints_dump-" in it.toString() }
|
||||
val json = ZipInputStream(zipFile.inputStream()).use { zip ->
|
||||
@ -350,7 +350,7 @@ class InteractiveShellIntegrationTest {
|
||||
assertThrows<TimeoutException> {
|
||||
alice.rpc.startFlow(::WaitForStateConsumptionFlow, stateRefs).returnValue.getOrThrow(10.seconds)
|
||||
}
|
||||
InteractiveShell.runDumpCheckpoints(alice.rpc as InternalCordaRPCOps)
|
||||
alice.checkpointsRpc.use { InteractiveShell.runDumpCheckpoints(it) }
|
||||
val zipFile = (alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list().first { "checkpoints_dump-" in it.toString() }
|
||||
val json = ZipInputStream(zipFile.inputStream()).use { zip ->
|
||||
zip.nextEntry
|
||||
@ -390,7 +390,7 @@ class InteractiveShellIntegrationTest {
|
||||
Thread.sleep(5000)
|
||||
|
||||
mockRenderPrintWriter()
|
||||
InteractiveShell.runDumpCheckpoints(aliceNode.rpc as InternalCordaRPCOps)
|
||||
aliceNode.checkpointsRpc.use { InteractiveShell.runDumpCheckpoints(it) }
|
||||
|
||||
val zipFile = (aliceNode.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list().first { "checkpoints_dump-" in it.toString() }
|
||||
val json = ZipInputStream(zipFile.inputStream()).use { zip ->
|
||||
|
@ -1,14 +1,22 @@
|
||||
package net.corda.tools.shell;
|
||||
|
||||
import net.corda.core.internal.messaging.AttachmentTrustInfoRPCOps;
|
||||
import org.crsh.cli.Command;
|
||||
import org.crsh.cli.Man;
|
||||
import org.crsh.cli.Named;
|
||||
import org.crsh.cli.Usage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static net.corda.tools.shell.InteractiveShell.runAttachmentTrustInfoView;
|
||||
|
||||
@Named("attachments")
|
||||
public class AttachmentShellCommand extends InteractiveShellCommand {
|
||||
public class AttachmentShellCommand extends InteractiveShellCommand<AttachmentTrustInfoRPCOps> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Class<AttachmentTrustInfoRPCOps> getRpcOpsClass() {
|
||||
return AttachmentTrustInfoRPCOps.class;
|
||||
}
|
||||
|
||||
@Command
|
||||
@Man("Displays the trusted CorDapp attachments that have been manually installed or received over the network")
|
||||
|
@ -1,14 +1,22 @@
|
||||
package net.corda.tools.shell;
|
||||
|
||||
import net.corda.core.internal.messaging.FlowManagerRPCOps;
|
||||
import org.crsh.cli.Command;
|
||||
import org.crsh.cli.Man;
|
||||
import org.crsh.cli.Named;
|
||||
import org.crsh.cli.Usage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static net.corda.tools.shell.InteractiveShell.*;
|
||||
|
||||
@Named("checkpoints")
|
||||
public class CheckpointShellCommand extends InteractiveShellCommand {
|
||||
public class CheckpointShellCommand extends InteractiveShellCommand<FlowManagerRPCOps> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Class<FlowManagerRPCOps> getRpcOpsClass() {
|
||||
return FlowManagerRPCOps.class;
|
||||
}
|
||||
|
||||
@Command
|
||||
@Man("Outputs the contents of all checkpoints as json to be manually reviewed")
|
||||
|
@ -27,7 +27,7 @@ import static net.corda.tools.shell.InteractiveShell.runStateMachinesView;
|
||||
"flow constructors (the right one is picked automatically) are then specified using the same syntax as for the run command."
|
||||
)
|
||||
@Named("flow")
|
||||
public class FlowShellCommand extends InteractiveShellCommand {
|
||||
public class FlowShellCommand extends CordaRpcOpsShellCommand {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FlowShellCommand.class);
|
||||
|
||||
|
@ -19,7 +19,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Named("hashLookup")
|
||||
public class HashLookupShellCommand extends InteractiveShellCommand {
|
||||
public class HashLookupShellCommand extends CordaRpcOpsShellCommand {
|
||||
private static Logger logger = LoggerFactory.getLogger(HashLookupShellCommand.class);
|
||||
private static final String manualText ="Checks if a transaction matching a specified Id hash value is recorded on this node.\n\n" +
|
||||
"Both the transaction Id and the hashed value of a transaction Id (as returned by the Notary in case of a double-spend) is a valid input.\n" +
|
||||
|
@ -18,7 +18,7 @@ import java.util.Map;
|
||||
@Man("Allows you to see and update the format that's currently used for the commands' output.")
|
||||
@Usage("Allows you to see and update the format that's currently used for the commands' output.")
|
||||
@Named("output-format")
|
||||
public class OutputFormatCommand extends InteractiveShellCommand {
|
||||
public class OutputFormatCommand extends CordaRpcOpsShellCommand {
|
||||
|
||||
public OutputFormatCommand() {}
|
||||
|
||||
|
@ -25,7 +25,7 @@ import static java.util.Comparator.comparing;
|
||||
// is the closest you can get in Kotlin to raw types.
|
||||
|
||||
@Named("run")
|
||||
public class RunShellCommand extends InteractiveShellCommand {
|
||||
public class RunShellCommand extends CordaRpcOpsShellCommand {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RunShellCommand.class);
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
package net.corda.tools.shell;
|
||||
|
||||
import net.corda.core.messaging.RPCOps;
|
||||
import org.crsh.auth.AuthInfo;
|
||||
|
||||
public interface SshAuthInfo extends AuthInfo {
|
||||
<T extends RPCOps> T getOrCreateRpcOps(Class<T> rpcOpsClass);
|
||||
}
|
@ -13,7 +13,7 @@ import java.util.*;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
@Named("start")
|
||||
public class StartShellCommand extends InteractiveShellCommand {
|
||||
public class StartShellCommand extends CordaRpcOpsShellCommand {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(StartShellCommand.class);
|
||||
|
||||
|
@ -1,14 +1,13 @@
|
||||
package net.corda.tools.shell
|
||||
|
||||
import net.corda.client.rpc.CordaRPCConnection
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
||||
import org.crsh.auth.AuthInfo
|
||||
import org.crsh.auth.AuthenticationPlugin
|
||||
import org.crsh.plugin.CRaSHPlugin
|
||||
|
||||
class CordaAuthenticationPlugin(private val makeRPCConnection: (username: String, credential: String) -> CordaRPCConnection) : CRaSHPlugin<AuthenticationPlugin<String>>(), AuthenticationPlugin<String> {
|
||||
internal class CordaAuthenticationPlugin(private val rpcOpsProducer: RPCOpsProducer) : CRaSHPlugin<AuthenticationPlugin<String>>(), AuthenticationPlugin<String> {
|
||||
|
||||
companion object {
|
||||
private val logger = loggerFor<CordaAuthenticationPlugin>()
|
||||
@ -24,9 +23,10 @@ class CordaAuthenticationPlugin(private val makeRPCConnection: (username: String
|
||||
return AuthInfo.UNSUCCESSFUL
|
||||
}
|
||||
try {
|
||||
val connection = makeRPCConnection(username, credential)
|
||||
val ops = connection.proxy as InternalCordaRPCOps
|
||||
return CordaSSHAuthInfo(true, ops, isSsh = true, rpcConn = connection)
|
||||
val cordaSSHAuthInfo = CordaSSHAuthInfo(rpcOpsProducer, username, credential, isSsh = true)
|
||||
// We cannot guarantee authentication happened successfully till `RCPClient` session been established, hence doing a dummy call
|
||||
cordaSSHAuthInfo.getOrCreateRpcOps(CordaRPCOps::class.java).protocolVersion
|
||||
return cordaSSHAuthInfo
|
||||
} catch (e: ActiveMQSecurityException) {
|
||||
logger.warn(e.message)
|
||||
} catch (e: Exception) {
|
||||
|
@ -8,6 +8,6 @@ class CordaDisconnectPlugin : CRaSHPlugin<DisconnectPlugin>(), DisconnectPlugin
|
||||
override fun getImplementation() = this
|
||||
|
||||
override fun onDisconnect(userName: String?, authInfo: AuthInfo?) {
|
||||
(authInfo as? CordaSSHAuthInfo)?.rpcConn?.forceClose()
|
||||
(authInfo as? CordaSSHAuthInfo)?.cleanUp()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
package net.corda.tools.shell
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.type.TypeFactory
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
|
||||
internal abstract class CordaRpcOpsShellCommand : InteractiveShellCommand<CordaRPCOps>() {
|
||||
override val rpcOpsClass: Class<out CordaRPCOps> = CordaRPCOps::class.java
|
||||
|
||||
fun objectMapper(classLoader: ClassLoader?): ObjectMapper {
|
||||
val om = createYamlInputMapper()
|
||||
if (classLoader != null) {
|
||||
om.typeFactory = TypeFactory.defaultInstance().withClassLoader(classLoader)
|
||||
}
|
||||
return om
|
||||
}
|
||||
|
||||
private fun createYamlInputMapper(): ObjectMapper {
|
||||
val rpcOps = ops()
|
||||
return InteractiveShell.createYamlInputMapper(rpcOps)
|
||||
}
|
||||
}
|
@ -1,17 +1,53 @@
|
||||
package net.corda.tools.shell
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import net.corda.client.rpc.CordaRPCConnection
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import net.corda.tools.shell.InteractiveShell.createYamlInputMapper
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.github.benmanes.caffeine.cache.RemovalListener
|
||||
import com.google.common.util.concurrent.MoreExecutors
|
||||
import net.corda.client.rpc.RPCConnection
|
||||
import net.corda.core.internal.utilities.InvocationHandlerTemplate
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.tools.shell.utlities.ANSIProgressRenderer
|
||||
import org.crsh.auth.AuthInfo
|
||||
import java.lang.reflect.Proxy
|
||||
|
||||
class CordaSSHAuthInfo(val successful: Boolean, val rpcOps: InternalCordaRPCOps, val ansiProgressRenderer: ANSIProgressRenderer? = null,
|
||||
val isSsh: Boolean = false, val rpcConn: CordaRPCConnection? = null) : AuthInfo {
|
||||
override fun isSuccessful(): Boolean = successful
|
||||
internal class CordaSSHAuthInfo(private val rpcOpsProducer: RPCOpsProducer,
|
||||
private val username: String, private val credential: String, val ansiProgressRenderer: ANSIProgressRenderer? = null,
|
||||
val isSsh: Boolean = false) : SshAuthInfo {
|
||||
override fun isSuccessful(): Boolean = true
|
||||
|
||||
val yamlInputMapper: ObjectMapper by lazy {
|
||||
createYamlInputMapper(rpcOps)
|
||||
/**
|
||||
* It is necessary to have a cache to prevent creation of too many proxies for the same class. Proxy ensures that RPC connections gracefully
|
||||
* closed when cache entry is eliminated
|
||||
*/
|
||||
private val proxiesCache = Caffeine.newBuilder()
|
||||
.maximumSize(10)
|
||||
.removalListener(RemovalListener<Class<out RPCOps>, Pair<RPCOps, RPCConnection<RPCOps>>> { _, value, _ -> value?.second?.close() })
|
||||
.executor(MoreExecutors.directExecutor())
|
||||
.build(CacheLoader<Class<out RPCOps>, Pair<RPCOps, RPCConnection<RPCOps>>> { key -> createRpcOps(key) })
|
||||
|
||||
override fun <T : RPCOps> getOrCreateRpcOps(rpcOpsClass: Class<T>): T {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return proxiesCache.get(rpcOpsClass)!!.first as T
|
||||
}
|
||||
|
||||
fun cleanUp() {
|
||||
proxiesCache.asMap().forEach {
|
||||
proxiesCache.invalidate(it.key)
|
||||
it.value.second.forceClose()
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : RPCOps> createRpcOps(rpcOpsClass: Class<out T>): Pair<T, RPCConnection<T>> {
|
||||
val producerResult = rpcOpsProducer(username, credential, rpcOpsClass)
|
||||
val anotherProxy = proxyRPCOps(producerResult.proxy, rpcOpsClass)
|
||||
return anotherProxy to producerResult
|
||||
}
|
||||
|
||||
private fun <T : RPCOps> proxyRPCOps(instance: T, rpcOpsClass: Class<out T>): T {
|
||||
require(rpcOpsClass.isInterface) { "$rpcOpsClass must be an interface" }
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return Proxy.newProxyInstance(rpcOpsClass.classLoader, arrayOf(rpcOpsClass), object : InvocationHandlerTemplate {
|
||||
override val delegate = instance
|
||||
}) as T
|
||||
}
|
||||
}
|
@ -9,11 +9,8 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator
|
||||
import net.corda.client.jackson.JacksonSupport
|
||||
import net.corda.client.jackson.StringToMethodCallParser
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.client.rpc.CordaRPCClientConfiguration
|
||||
import net.corda.client.rpc.CordaRPCConnection
|
||||
import net.corda.client.rpc.GracefulReconnect
|
||||
import net.corda.client.rpc.PermissionException
|
||||
import net.corda.client.rpc.RPCConnection
|
||||
import net.corda.client.rpc.internal.RPCUtils.isShutdownMethodName
|
||||
import net.corda.client.rpc.notUsed
|
||||
import net.corda.core.CordaException
|
||||
@ -27,7 +24,8 @@ import net.corda.core.internal.concurrent.doneFuture
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import net.corda.core.internal.messaging.AttachmentTrustInfoRPCOps
|
||||
import net.corda.core.internal.messaging.FlowManagerRPCOps
|
||||
import net.corda.core.internal.packageName_
|
||||
import net.corda.core.internal.rootCause
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
@ -72,11 +70,10 @@ import java.lang.reflect.ParameterizedType
|
||||
import java.lang.reflect.Type
|
||||
import java.lang.reflect.UndeclaredThrowableException
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import java.util.Properties
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.Future
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
// TODO: Add command history.
|
||||
@ -95,9 +92,9 @@ const val STANDALONE_SHELL_PERMISSION = "ALL"
|
||||
@Suppress("MaxLineLength")
|
||||
object InteractiveShell {
|
||||
private val log = LoggerFactory.getLogger(javaClass)
|
||||
private lateinit var makeRPCConnection: (username: String, password: String) -> CordaRPCConnection
|
||||
private lateinit var ops: InternalCordaRPCOps
|
||||
private lateinit var rpcConn: CordaRPCConnection
|
||||
private lateinit var rpcOpsProducer: RPCOpsProducer
|
||||
private lateinit var startupValidation: Lazy<CordaRPCOps>
|
||||
private var rpcConn: RPCConnection<CordaRPCOps>? = null
|
||||
private var shell: Shell? = null
|
||||
private var classLoader: ClassLoader? = null
|
||||
private lateinit var shellConfiguration: ShellConfiguration
|
||||
@ -113,26 +110,7 @@ object InteractiveShell {
|
||||
}
|
||||
|
||||
fun startShell(configuration: ShellConfiguration, classLoader: ClassLoader? = null, standalone: Boolean = false) {
|
||||
makeRPCConnection = { username: String, password: String ->
|
||||
val connection = if (standalone) {
|
||||
CordaRPCClient(
|
||||
configuration.hostAndPort,
|
||||
configuration.ssl,
|
||||
classLoader
|
||||
).start(username, password, gracefulReconnect = GracefulReconnect())
|
||||
} else {
|
||||
CordaRPCClient(
|
||||
hostAndPort = configuration.hostAndPort,
|
||||
configuration = CordaRPCClientConfiguration.DEFAULT.copy(
|
||||
maxReconnectAttempts = 1
|
||||
),
|
||||
sslConfiguration = configuration.ssl,
|
||||
classLoader = classLoader
|
||||
).start(username, password)
|
||||
}
|
||||
rpcConn = connection
|
||||
connection
|
||||
}
|
||||
rpcOpsProducer = DefaultRPCOpsProducer(configuration, classLoader, standalone)
|
||||
launchShell(configuration, standalone, classLoader)
|
||||
}
|
||||
|
||||
@ -253,7 +231,7 @@ object InteractiveShell {
|
||||
// Don't use the Java language plugin (we may not have tools.jar available at runtime), this
|
||||
// will cause any commands using JIT Java compilation to be suppressed. In CRaSH upstream that
|
||||
// is only the 'jmx' command.
|
||||
return super.getPlugins().filterNot { it is JavaLanguage } + CordaAuthenticationPlugin(makeRPCConnection) +
|
||||
return super.getPlugins().filterNot { it is JavaLanguage } + CordaAuthenticationPlugin(rpcOpsProducer) +
|
||||
CordaDisconnectPlugin()
|
||||
}
|
||||
}
|
||||
@ -262,15 +240,20 @@ object InteractiveShell {
|
||||
context.refresh()
|
||||
this.config = config
|
||||
start(context)
|
||||
val rpcOps = { username: String, password: String -> makeRPCConnection(username, password).proxy as InternalCordaRPCOps }
|
||||
ops = makeRPCOps(rpcOps, localUserName, localUserPassword)
|
||||
return context.getPlugin(ShellFactory::class.java).create(null, CordaSSHAuthInfo(false, ops,
|
||||
StdoutANSIProgressRenderer), shellSafety)
|
||||
startupValidation = lazy {
|
||||
rpcOpsProducer(localUserName, localUserPassword, CordaRPCOps::class.java).let {
|
||||
rpcConn = it
|
||||
it.proxy
|
||||
}
|
||||
}
|
||||
// For local shell create an artificial authInfo with super user permissions
|
||||
val authInfo = CordaSSHAuthInfo(rpcOpsProducer, localUserName, localUserPassword, StdoutANSIProgressRenderer)
|
||||
return context.getPlugin(ShellFactory::class.java).create(null, authInfo, shellSafety)
|
||||
}
|
||||
}
|
||||
|
||||
fun nodeInfo() = try {
|
||||
ops.nodeInfo()
|
||||
startupValidation.value.nodeInfo()
|
||||
} catch (e: UndeclaredThrowableException) {
|
||||
throw e.cause ?: e
|
||||
}
|
||||
@ -572,13 +555,13 @@ object InteractiveShell {
|
||||
@JvmStatic
|
||||
fun runAttachmentTrustInfoView(
|
||||
out: RenderPrintWriter,
|
||||
rpcOps: InternalCordaRPCOps
|
||||
rpcOps: AttachmentTrustInfoRPCOps
|
||||
): Any {
|
||||
return AttachmentTrustTable(out, rpcOps.attachmentTrustInfos)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun runDumpCheckpoints(rpcOps: InternalCordaRPCOps) {
|
||||
fun runDumpCheckpoints(rpcOps: FlowManagerRPCOps) {
|
||||
rpcOps.dumpCheckpoints()
|
||||
}
|
||||
|
||||
@ -682,7 +665,7 @@ object InteractiveShell {
|
||||
latch.await()
|
||||
// Unsubscribe or we hold up the shutdown
|
||||
subscription.unsubscribe()
|
||||
rpcConn.forceClose()
|
||||
rpcConn?.forceClose()
|
||||
onExit.invoke()
|
||||
} catch (e: InterruptedException) {
|
||||
// Cancelled whilst draining flows. So let's carry on from here
|
||||
@ -690,7 +673,7 @@ object InteractiveShell {
|
||||
display { println("...cancelled clean shutdown.") }
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
display { println("RPC failed: ${e.rootCause}", Color.red) }
|
||||
display { println("RPC failed: ${e.rootCause}", Decoration.bold, Color.red) }
|
||||
} finally {
|
||||
InputStreamSerializer.invokeContext = null
|
||||
InputStreamDeserializer.closeAll()
|
||||
|
@ -1,23 +1,24 @@
|
||||
package net.corda.tools.shell
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.type.TypeFactory
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import org.crsh.command.BaseCommand
|
||||
import org.crsh.shell.impl.command.CRaSHSession
|
||||
|
||||
/**
|
||||
* Simply extends CRaSH BaseCommand to add easy access to the RPC ops class.
|
||||
*/
|
||||
open class InteractiveShellCommand : BaseCommand() {
|
||||
fun ops() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).rpcOps
|
||||
fun ansiProgressRenderer() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).ansiProgressRenderer
|
||||
fun objectMapper(classLoader: ClassLoader?): ObjectMapper {
|
||||
val om = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).yamlInputMapper
|
||||
if (classLoader != null) {
|
||||
om.typeFactory = TypeFactory.defaultInstance().withClassLoader(classLoader)
|
||||
}
|
||||
return om
|
||||
internal abstract class InteractiveShellCommand<T : RPCOps> : BaseCommand() {
|
||||
|
||||
abstract val rpcOpsClass: Class<out T>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun ops(): T {
|
||||
val cRaSHSession = context.session as CRaSHSession
|
||||
val authInfo = cRaSHSession.authInfo as SshAuthInfo
|
||||
return authInfo.getOrCreateRpcOps(rpcOpsClass)
|
||||
}
|
||||
|
||||
fun ansiProgressRenderer() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).ansiProgressRenderer
|
||||
|
||||
fun isSsh() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).isSsh
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
package net.corda.tools.shell
|
||||
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.client.rpc.CordaRPCClientConfiguration
|
||||
import net.corda.client.rpc.GracefulReconnect
|
||||
import net.corda.client.rpc.RPCConnection
|
||||
import net.corda.client.rpc.internal.RPCClient
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.RPCOps
|
||||
|
||||
internal interface RPCOpsProducer {
|
||||
/**
|
||||
* Returns [RPCConnection] of underlying proxy. Proxy can be obtained at any time by calling [RPCConnection.proxy]
|
||||
*/
|
||||
operator fun <T : RPCOps> invoke(username: String?, credential: String?, rpcOpsClass: Class<T>) : RPCConnection<T>
|
||||
}
|
||||
|
||||
internal class DefaultRPCOpsProducer(private val configuration: ShellConfiguration, private val classLoader: ClassLoader? = null, private val standalone: Boolean) : RPCOpsProducer {
|
||||
|
||||
override fun <T : RPCOps> invoke(username: String?, credential: String?, rpcOpsClass: Class<T>): RPCConnection<T> {
|
||||
|
||||
return if (rpcOpsClass == CordaRPCOps::class.java) {
|
||||
// For CordaRPCOps we are using CordaRPCClient
|
||||
val connection = if (standalone) {
|
||||
CordaRPCClient(
|
||||
configuration.hostAndPort,
|
||||
configuration.ssl,
|
||||
classLoader
|
||||
).start(username!!, credential!!, gracefulReconnect = GracefulReconnect())
|
||||
} else {
|
||||
CordaRPCClient(
|
||||
hostAndPort = configuration.hostAndPort,
|
||||
configuration = CordaRPCClientConfiguration.DEFAULT.copy(
|
||||
maxReconnectAttempts = 1
|
||||
),
|
||||
sslConfiguration = configuration.ssl,
|
||||
classLoader = classLoader
|
||||
).start(username!!, credential!!)
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
connection as RPCConnection<T>
|
||||
} else {
|
||||
// For other types "plain" RPCClient is used
|
||||
val rpcClient = RPCClient<T>(configuration.hostAndPort, configuration.ssl)
|
||||
val connection = rpcClient.start(rpcOpsClass, username!!, credential!!)
|
||||
connection
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package net.corda.tools.shell
|
||||
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.lang.reflect.Proxy
|
||||
|
||||
fun makeRPCOps(getCordaRPCOps: (username: String, credential: String) -> InternalCordaRPCOps, username: String, credential: String): InternalCordaRPCOps {
|
||||
val cordaRPCOps: InternalCordaRPCOps by lazy {
|
||||
getCordaRPCOps(username, credential)
|
||||
}
|
||||
|
||||
return Proxy.newProxyInstance(InternalCordaRPCOps::class.java.classLoader, arrayOf(InternalCordaRPCOps::class.java)) { _, method, args ->
|
||||
try {
|
||||
method.invoke(cordaRPCOps, *(args ?: arrayOf()))
|
||||
} catch (e: InvocationTargetException) {
|
||||
// Unpack exception.
|
||||
throw e.targetException
|
||||
}
|
||||
} as InternalCordaRPCOps
|
||||
}
|
@ -18,7 +18,7 @@ import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.messaging.InternalCordaRPCOps
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.FlowProgressHandleImpl
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
@ -42,7 +42,7 @@ import kotlin.test.assertFailsWith
|
||||
|
||||
class InteractiveShellTest {
|
||||
lateinit var inputObjectMapper: ObjectMapper
|
||||
lateinit var cordaRpcOps: InternalCordaRPCOps
|
||||
lateinit var cordaRpcOps: CordaRPCOps
|
||||
lateinit var invocationContext: InvocationContext<Map<Any, Any>>
|
||||
lateinit var printWriter: RenderPrintWriter
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user