From 82ece34ac827dbee1479e3fc5c5f1ce378f049ac Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Fri, 2 Feb 2018 17:09:08 +0100 Subject: [PATCH] Tweak the way invocation context is integrated to reduce the pain for devs using the old API (#2447) Minor tweaks to the invocation context code. 1) Un-deprecate FlowInitiator, move the deprecation to the field. This eliminates large numbers of warnings and means developers are warned only once in the place where they obtain one. 2) Add documentation for StateMachineInfo and create a type alias to give it a better name in an ABI compatible way. 3) Improve markup on InvocationContext 4) Rename field from just "context" to "invocationContext" (Context is vague) --- .ci/api-current.txt | 40 ++++++------ .../corda/client/jfx/NodeMonitorModelTest.kt | 14 ++-- .../corda/client/rpc/CordaRPCClientTest.kt | 14 ++-- .../net/corda/core/context/AuthServiceId.kt | 9 --- .../corda/core/context/InvocationContext.kt | 64 +++++++++---------- .../net/corda/core/flows/FlowInitiator.kt | 42 +++++++++--- .../kotlin/net/corda/core/flows/FlowLogic.kt | 12 ++-- .../net/corda/core/messaging/CordaRPCOps.kt | 44 ++++++------- .../corda/node/internal/CordaRPCOpsImpl.kt | 12 ++-- .../services/events/NodeSchedulerService.kt | 4 +- .../node/shell/FlowWatchPrintingSubscriber.kt | 2 +- .../corda/node/internal/CordaServiceTest.kt | 4 +- .../services/events/ScheduledFlowTests.kt | 4 +- .../net/corda/testing/node/NodeTestUtils.kt | 4 +- 14 files changed, 139 insertions(+), 130 deletions(-) delete mode 100644 core/src/main/kotlin/net/corda/core/context/AuthServiceId.kt diff --git a/.ci/api-current.txt b/.ci/api-current.txt index 0fd2a15fc9..8d485ec906 100644 --- a/.ci/api-current.txt +++ b/.ci/api-current.txt @@ -96,21 +96,21 @@ public static final class net.corda.core.context.Actor$Companion extends java.la public String toString() ## @net.corda.core.serialization.CordaSerializable public final class net.corda.core.context.InvocationContext extends java.lang.Object - public (net.corda.core.context.Origin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor) - @org.jetbrains.annotations.NotNull public final net.corda.core.context.Origin component1() + public (net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor) + @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationOrigin component1() @org.jetbrains.annotations.NotNull public final net.corda.core.context.Trace component2() @org.jetbrains.annotations.Nullable public final net.corda.core.context.Actor component3() @org.jetbrains.annotations.Nullable public final net.corda.core.context.Trace component4() @org.jetbrains.annotations.Nullable public final net.corda.core.context.Actor component5() - @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext copy(net.corda.core.context.Origin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor) + @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext copy(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor) public boolean equals(Object) @org.jetbrains.annotations.Nullable public final net.corda.core.context.Actor getActor() @org.jetbrains.annotations.Nullable public final net.corda.core.context.Trace getExternalTrace() @org.jetbrains.annotations.Nullable public final net.corda.core.context.Actor getImpersonatedActor() - @org.jetbrains.annotations.NotNull public final net.corda.core.context.Origin getOrigin() + @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationOrigin getOrigin() @org.jetbrains.annotations.NotNull public final net.corda.core.context.Trace getTrace() public int hashCode() - @kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.context.InvocationContext newInstance(net.corda.core.context.Origin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor) + @kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.context.InvocationContext newInstance(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor) @kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.context.InvocationContext peer(net.corda.core.identity.CordaX500Name, net.corda.core.context.Trace, net.corda.core.context.Trace, net.corda.core.context.Actor) @org.jetbrains.annotations.NotNull public final java.security.Principal principal() @kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.context.InvocationContext rpc(net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Trace, net.corda.core.context.Actor) @@ -121,49 +121,49 @@ public static final class net.corda.core.context.Actor$Companion extends java.la public static final net.corda.core.context.InvocationContext$Companion Companion ## public static final class net.corda.core.context.InvocationContext$Companion extends java.lang.Object - @kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext newInstance(net.corda.core.context.Origin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor) + @kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext newInstance(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor) @kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext peer(net.corda.core.identity.CordaX500Name, net.corda.core.context.Trace, net.corda.core.context.Trace, net.corda.core.context.Actor) @kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext rpc(net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Trace, net.corda.core.context.Actor) @kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext scheduled(net.corda.core.contracts.ScheduledStateRef, net.corda.core.context.Trace, net.corda.core.context.Trace) @kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext service(String, net.corda.core.identity.CordaX500Name, net.corda.core.context.Trace, net.corda.core.context.Trace) @kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext shell(net.corda.core.context.Trace, net.corda.core.context.Trace) ## -@net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.context.Origin extends java.lang.Object +@net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.context.InvocationOrigin extends java.lang.Object @org.jetbrains.annotations.NotNull public abstract java.security.Principal principal() ## -@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.Origin$Peer extends net.corda.core.context.Origin +@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.InvocationOrigin$Peer extends net.corda.core.context.InvocationOrigin public (net.corda.core.identity.CordaX500Name) @org.jetbrains.annotations.NotNull public final net.corda.core.identity.CordaX500Name component1() - @org.jetbrains.annotations.NotNull public final net.corda.core.context.Origin$Peer copy(net.corda.core.identity.CordaX500Name) + @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationOrigin$Peer copy(net.corda.core.identity.CordaX500Name) public boolean equals(Object) @org.jetbrains.annotations.NotNull public final net.corda.core.identity.CordaX500Name getParty() public int hashCode() @org.jetbrains.annotations.NotNull public java.security.Principal principal() public String toString() ## -@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.Origin$RPC extends net.corda.core.context.Origin +@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.InvocationOrigin$RPC extends net.corda.core.context.InvocationOrigin public (net.corda.core.context.Actor) - @org.jetbrains.annotations.NotNull public final net.corda.core.context.Origin$RPC copy(net.corda.core.context.Actor) + @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationOrigin$RPC copy(net.corda.core.context.Actor) public boolean equals(Object) public int hashCode() @org.jetbrains.annotations.NotNull public java.security.Principal principal() public String toString() ## -@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.Origin$Scheduled extends net.corda.core.context.Origin +@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.InvocationOrigin$Scheduled extends net.corda.core.context.InvocationOrigin public (net.corda.core.contracts.ScheduledStateRef) @org.jetbrains.annotations.NotNull public final net.corda.core.contracts.ScheduledStateRef component1() - @org.jetbrains.annotations.NotNull public final net.corda.core.context.Origin$Scheduled copy(net.corda.core.contracts.ScheduledStateRef) + @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationOrigin$Scheduled copy(net.corda.core.contracts.ScheduledStateRef) public boolean equals(Object) @org.jetbrains.annotations.NotNull public final net.corda.core.contracts.ScheduledStateRef getScheduledState() public int hashCode() @org.jetbrains.annotations.NotNull public java.security.Principal principal() public String toString() ## -@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.Origin$Service extends net.corda.core.context.Origin +@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.InvocationOrigin$Service extends net.corda.core.context.InvocationOrigin public (String, net.corda.core.identity.CordaX500Name) @org.jetbrains.annotations.NotNull public final String component1() @org.jetbrains.annotations.NotNull public final net.corda.core.identity.CordaX500Name component2() - @org.jetbrains.annotations.NotNull public final net.corda.core.context.Origin$Service copy(String, net.corda.core.identity.CordaX500Name) + @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationOrigin$Service copy(String, net.corda.core.identity.CordaX500Name) public boolean equals(Object) @org.jetbrains.annotations.NotNull public final net.corda.core.identity.CordaX500Name getOwningLegalIdentity() @org.jetbrains.annotations.NotNull public final String getServiceClassName() @@ -171,9 +171,9 @@ public static final class net.corda.core.context.InvocationContext$Companion ext @org.jetbrains.annotations.NotNull public java.security.Principal principal() public String toString() ## -@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.Origin$Shell extends net.corda.core.context.Origin +@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.InvocationOrigin$Shell extends net.corda.core.context.InvocationOrigin @org.jetbrains.annotations.NotNull public java.security.Principal principal() - public static final net.corda.core.context.Origin$Shell INSTANCE + public static final net.corda.core.context.InvocationOrigin$Shell INSTANCE ## @net.corda.core.serialization.CordaSerializable public final class net.corda.core.context.Trace extends java.lang.Object public (net.corda.core.context.Trace$InvocationId, net.corda.core.context.Trace$SessionId) @@ -1168,6 +1168,7 @@ public static final class net.corda.core.flows.FinalityFlow$Companion extends ja public String toString() ## @net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.flows.FlowInitiator extends java.lang.Object implements java.security.Principal + @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext getInvocationContext() ## @net.corda.core.serialization.CordaSerializable public static final class net.corda.core.flows.FlowInitiator$Peer extends net.corda.core.flows.FlowInitiator public (net.corda.core.identity.Party) @@ -1669,15 +1670,14 @@ public @interface net.corda.core.messaging.RPCReturnsObservables @org.jetbrains.annotations.NotNull public final String component2() @org.jetbrains.annotations.NotNull public final net.corda.core.flows.FlowInitiator component3() @org.jetbrains.annotations.Nullable public final net.corda.core.messaging.DataFeed component4() - @org.jetbrains.annotations.Nullable public final net.corda.core.context.InvocationContext component5() - @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext context() + @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext component5() @org.jetbrains.annotations.NotNull public final net.corda.core.messaging.StateMachineInfo copy(net.corda.core.flows.StateMachineRunId, String, net.corda.core.flows.FlowInitiator, net.corda.core.messaging.DataFeed) @org.jetbrains.annotations.NotNull public final net.corda.core.messaging.StateMachineInfo copy(net.corda.core.flows.StateMachineRunId, String, net.corda.core.flows.FlowInitiator, net.corda.core.messaging.DataFeed, net.corda.core.context.InvocationContext) public boolean equals(Object) - @org.jetbrains.annotations.Nullable public final net.corda.core.context.InvocationContext getContext() @org.jetbrains.annotations.NotNull public final String getFlowLogicClassName() @org.jetbrains.annotations.NotNull public final net.corda.core.flows.StateMachineRunId getId() @org.jetbrains.annotations.NotNull public final net.corda.core.flows.FlowInitiator getInitiator() + @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext getInvocationContext() @org.jetbrains.annotations.Nullable public final net.corda.core.messaging.DataFeed getProgressTrackerStepAndUpdates() public int hashCode() @org.jetbrains.annotations.NotNull public String toString() diff --git a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt index f8f5c58dcf..81006e9614 100644 --- a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt +++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt @@ -2,7 +2,7 @@ package net.corda.client.jfx import net.corda.client.jfx.model.NodeMonitorModel import net.corda.client.jfx.model.ProgressTrackingEvent -import net.corda.core.context.Origin +import net.corda.core.context.InvocationOrigin import net.corda.core.contracts.Amount import net.corda.core.contracts.ContractState import net.corda.core.crypto.isFulfilledBy @@ -148,8 +148,8 @@ class NodeMonitorModelTest { // ISSUE expect { add: StateMachineUpdate.Added -> issueSmId = add.id - val context = add.stateMachineInfo.context() - require(context.origin is Origin.RPC && context.principal().name == "user1") + val context = add.stateMachineInfo.invocationContext + require(context.origin is InvocationOrigin.RPC && context.principal().name == "user1") }, expect { remove: StateMachineUpdate.Removed -> require(remove.id == issueSmId) @@ -157,8 +157,8 @@ class NodeMonitorModelTest { // MOVE - N.B. There are other framework flows that happen in parallel for the remote resolve transactions flow expect(match = { it.stateMachineInfo.flowLogicClassName == CashPaymentFlow::class.java.name }) { add: StateMachineUpdate.Added -> moveSmId = add.id - val context = add.stateMachineInfo.context() - require(context.origin is Origin.RPC && context.principal().name == "user1") + val context = add.stateMachineInfo.invocationContext + require(context.origin is InvocationOrigin.RPC && context.principal().name == "user1") }, expect(match = { it is StateMachineUpdate.Removed && it.id == moveSmId }) { } @@ -169,8 +169,8 @@ class NodeMonitorModelTest { sequence( // MOVE expect { add: StateMachineUpdate.Added -> - val context = add.stateMachineInfo.context() - require(context.origin is Origin.Peer && aliceNode.isLegalIdentity(aliceNode.identityFromX500Name((context.origin as Origin.Peer).party))) + val context = add.stateMachineInfo.invocationContext + require(context.origin is InvocationOrigin.Peer && aliceNode.isLegalIdentity(aliceNode.identityFromX500Name((context.origin as InvocationOrigin.Peer).party))) } ) } diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index bfb77b4aeb..b95480b95b 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -154,11 +154,11 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C }, expect { update: StateMachineUpdate.Added -> checkRpcNotification(update.stateMachineInfo, rpcUser.username, historicalIds, externalTrace, impersonatedActor) - sessionId = update.stateMachineInfo.context().trace.sessionId + sessionId = update.stateMachineInfo.invocationContext.trace.sessionId }, expect { update: StateMachineUpdate.Added -> checkRpcNotification(update.stateMachineInfo, rpcUser.username, historicalIds, externalTrace, impersonatedActor) - assertThat(update.stateMachineInfo.context().trace.sessionId).isEqualTo(sessionId) + assertThat(update.stateMachineInfo.invocationContext.trace.sessionId).isEqualTo(sessionId) } ) } @@ -166,15 +166,13 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C } private fun checkShellNotification(info: StateMachineInfo) { - - val context = info.context() - assertThat(context.origin).isInstanceOf(Origin.Shell::class.java) + val context = info.invocationContext + assertThat(context.origin).isInstanceOf(InvocationOrigin.Shell::class.java) } private fun checkRpcNotification(info: StateMachineInfo, rpcUsername: String, historicalIds: MutableSet, externalTrace: Trace?, impersonatedActor: Actor?) { - - val context = info.context() - assertThat(context.origin).isInstanceOf(Origin.RPC::class.java) + val context = info.invocationContext + assertThat(context.origin).isInstanceOf(InvocationOrigin.RPC::class.java) assertThat(context.externalTrace).isEqualTo(externalTrace) assertThat(context.impersonatedActor).isEqualTo(impersonatedActor) assertThat(context.actor?.id?.value).isEqualTo(rpcUsername) diff --git a/core/src/main/kotlin/net/corda/core/context/AuthServiceId.kt b/core/src/main/kotlin/net/corda/core/context/AuthServiceId.kt deleted file mode 100644 index 9edc2680ca..0000000000 --- a/core/src/main/kotlin/net/corda/core/context/AuthServiceId.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.corda.core.context - -import net.corda.core.serialization.CordaSerializable - -/** - * Authentication / Authorisation Service ID. - */ -@CordaSerializable -data class AuthServiceId(val value: String) \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/context/InvocationContext.kt b/core/src/main/kotlin/net/corda/core/context/InvocationContext.kt index 9ca93033c0..58c1544c21 100644 --- a/core/src/main/kotlin/net/corda/core/context/InvocationContext.kt +++ b/core/src/main/kotlin/net/corda/core/context/InvocationContext.kt @@ -9,52 +9,50 @@ import java.security.Principal * Models the information needed to trace an invocation in Corda. * Includes initiating actor, origin, trace information, and optional external trace information to correlate clients' IDs. * - * @param origin origin of the invocation. - * @param trace Corda invocation trace. - * @param actor acting agent of the invocation, used to derive the security principal. - * @param externalTrace optional external invocation trace for cross-system logs correlation. - * @param impersonatedActor optional impersonated actor, used for logging but not for authorisation. + * @property origin Origin of the invocation. + * @property trace Corda invocation trace. + * @property actor Acting agent of the invocation, used to derive the security principal. + * @property externalTrace Optional external invocation trace for cross-system logs correlation. + * @property impersonatedActor Optional impersonated actor, used for logging but not for authorisation. */ @CordaSerializable -data class InvocationContext(val origin: Origin, val trace: Trace, val actor: Actor?, val externalTrace: Trace? = null, val impersonatedActor: Actor? = null) { - +data class InvocationContext(val origin: InvocationOrigin, val trace: Trace, val actor: Actor?, val externalTrace: Trace? = null, val impersonatedActor: Actor? = null) { companion object { - /** * Creates an [InvocationContext] with a [Trace] that defaults to a [java.util.UUID] as value and [java.time.Instant.now] timestamp. */ @JvmStatic - fun newInstance(origin: Origin, trace: Trace = Trace.newInstance(), actor: Actor? = null, externalTrace: Trace? = null, impersonatedActor: Actor? = null) = InvocationContext(origin, trace, actor, externalTrace, impersonatedActor) + fun newInstance(origin: InvocationOrigin, trace: Trace = Trace.newInstance(), actor: Actor? = null, externalTrace: Trace? = null, impersonatedActor: Actor? = null) = InvocationContext(origin, trace, actor, externalTrace, impersonatedActor) /** - * Creates an [InvocationContext] with [Origin.RPC] origin. + * Creates an [InvocationContext] with [InvocationOrigin.RPC] origin. */ @JvmStatic - fun rpc(actor: Actor, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null, impersonatedActor: Actor? = null): InvocationContext = newInstance(Origin.RPC(actor), trace, actor, externalTrace, impersonatedActor) + fun rpc(actor: Actor, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null, impersonatedActor: Actor? = null): InvocationContext = newInstance(InvocationOrigin.RPC(actor), trace, actor, externalTrace, impersonatedActor) /** - * Creates an [InvocationContext] with [Origin.Peer] origin. + * Creates an [InvocationContext] with [InvocationOrigin.Peer] origin. */ @JvmStatic - fun peer(party: CordaX500Name, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null, impersonatedActor: Actor? = null): InvocationContext = newInstance(Origin.Peer(party), trace, null, externalTrace, impersonatedActor) + fun peer(party: CordaX500Name, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null, impersonatedActor: Actor? = null): InvocationContext = newInstance(InvocationOrigin.Peer(party), trace, null, externalTrace, impersonatedActor) /** - * Creates an [InvocationContext] with [Origin.Service] origin. + * Creates an [InvocationContext] with [InvocationOrigin.Service] origin. */ @JvmStatic - fun service(serviceClassName: String, owningLegalIdentity: CordaX500Name, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = newInstance(Origin.Service(serviceClassName, owningLegalIdentity), trace, null, externalTrace) + fun service(serviceClassName: String, owningLegalIdentity: CordaX500Name, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = newInstance(InvocationOrigin.Service(serviceClassName, owningLegalIdentity), trace, null, externalTrace) /** - * Creates an [InvocationContext] with [Origin.Scheduled] origin. + * Creates an [InvocationContext] with [InvocationOrigin.Scheduled] origin. */ @JvmStatic - fun scheduled(scheduledState: ScheduledStateRef, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = newInstance(Origin.Scheduled(scheduledState), trace, null, externalTrace) + fun scheduled(scheduledState: ScheduledStateRef, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = newInstance(InvocationOrigin.Scheduled(scheduledState), trace, null, externalTrace) /** - * Creates an [InvocationContext] with [Origin.Shell] origin. + * Creates an [InvocationContext] with [InvocationOrigin.Shell] origin. */ @JvmStatic - fun shell(trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = InvocationContext(Origin.Shell, trace, null, externalTrace) + fun shell(trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = InvocationContext(InvocationOrigin.Shell, trace, null, externalTrace) } /** @@ -83,11 +81,10 @@ data class Actor(val id: Id, val serviceId: AuthServiceId, val owningLegalIdenti } /** - * Invocation origin for tracing purposes. + * Represents the source of an action such as a flow start, an RPC, a shell command etc. */ @CordaSerializable -sealed class Origin { - +sealed class InvocationOrigin { /** * Returns the [Principal] for a given [Actor]. */ @@ -96,32 +93,28 @@ sealed class Origin { /** * Origin was an RPC call. */ - data class RPC(private val actor: Actor) : Origin() { - + data class RPC(private val actor: Actor) : InvocationOrigin() { override fun principal() = Principal { actor.id.value } } /** * Origin was a message sent by a [Peer]. */ - data class Peer(val party: CordaX500Name) : Origin() { - + data class Peer(val party: CordaX500Name) : InvocationOrigin() { override fun principal() = Principal { party.toString() } } /** * Origin was a Corda Service. */ - data class Service(val serviceClassName: String, val owningLegalIdentity: CordaX500Name) : Origin() { - + data class Service(val serviceClassName: String, val owningLegalIdentity: CordaX500Name) : InvocationOrigin() { override fun principal() = Principal { serviceClassName } } /** * Origin was a scheduled activity. */ - data class Scheduled(val scheduledState: ScheduledStateRef) : Origin() { - + data class Scheduled(val scheduledState: ScheduledStateRef) : InvocationOrigin() { override fun principal() = Principal { "Scheduler" } } @@ -129,8 +122,13 @@ sealed class Origin { /** * Origin was the Shell. */ - object Shell : Origin() { - + object Shell : InvocationOrigin() { override fun principal() = Principal { "Shell User" } } -} \ No newline at end of file +} + +/** + * Authentication / Authorisation Service ID. + */ +@CordaSerializable +data class AuthServiceId(val value: String) \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowInitiator.kt b/core/src/main/kotlin/net/corda/core/flows/FlowInitiator.kt index 8623169da8..206862d2ec 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowInitiator.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowInitiator.kt @@ -1,45 +1,69 @@ package net.corda.core.flows +import net.corda.core.context.Actor +import net.corda.core.context.AuthServiceId +import net.corda.core.context.InvocationContext +import net.corda.core.context.InvocationOrigin import net.corda.core.contracts.ScheduledStateRef +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable import java.security.Principal /** - * FlowInitiator holds information on who started the flow. We have different ways of doing that: via RPC [FlowInitiator.RPC], - * communication started by peer node [FlowInitiator.Peer], scheduled flows [FlowInitiator.Scheduled] - * or via the Corda Shell [FlowInitiator.Shell]. + * Please note that [FlowInitiator] has been superceded by [net.corda.core.context.InvocationContext], which offers + * more detail for the same event. + * + * FlowInitiator holds information on who started the flow. We have different ways of doing that: via [FlowInitiator.RPC], + * communication started by peer nodes ([FlowInitiator.Peer]), scheduled flows ([FlowInitiator.Scheduled]) + * or via the Corda Shell ([FlowInitiator.Shell]). */ -@Deprecated("Do not use these types. Future releases might remove them.") @CordaSerializable sealed class FlowInitiator : Principal { /** Started using [net.corda.core.messaging.CordaRPCOps.startFlowDynamic]. */ - @Deprecated("Do not use this type. Future releases might remove it.") data class RPC(val username: String) : FlowInitiator() { override fun getName(): String = username } /** Started when we get new session initiation request. */ - @Deprecated("Do not use this type. Future releases might remove it.") data class Peer(val party: Party) : FlowInitiator() { override fun getName(): String = party.name.toString() } /** Started by a CordaService. */ - @Deprecated("Do not use this type. Future releases might remove it.") data class Service(val serviceClassName: String) : FlowInitiator() { override fun getName(): String = serviceClassName } /** Started as scheduled activity. */ - @Deprecated("Do not use this type. Future releases might remove it.") data class Scheduled(val scheduledState: ScheduledStateRef) : FlowInitiator() { override fun getName(): String = "Scheduler" } // TODO When proper ssh access enabled, add username/use RPC? - @Deprecated("Do not use this type. Future releases might remove it.") object Shell : FlowInitiator() { override fun getName(): String = "Shell User" } + + /** + * Returns an [InvocationContext], which is equivalent to this object but expressed using the successor to this + * class hierarchy (which is now deprecated). The returned object has less information than it could have, so + * prefer to use fetch an invocation context directly if you can (e.g. in [net.corda.core.messaging.StateMachineInfo]) + */ + val invocationContext: InvocationContext get() { + val unknownName = CordaX500Name("UNKNOWN", "UNKNOWN", "GB") + var actor: Actor? = null + val origin: InvocationOrigin + when (this) { + is FlowInitiator.RPC -> { + actor = Actor(Actor.Id(this.username), AuthServiceId("UNKNOWN"), unknownName) + origin = InvocationOrigin.RPC(actor) + } + is FlowInitiator.Peer -> origin = InvocationOrigin.Peer(this.party.name) + is FlowInitiator.Service -> origin = InvocationOrigin.Service(this.serviceClassName, unknownName) + FlowInitiator.Shell -> origin = InvocationOrigin.Shell + is FlowInitiator.Scheduled -> origin = InvocationOrigin.Scheduled(this.scheduledState) + } + return InvocationContext.newInstance(origin = origin, actor = actor) + } } \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt index a10a3d3fe2..476521f0ec 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt @@ -42,7 +42,7 @@ import java.time.Instant * also has a version property to allow you to version your flow and enables a node to restrict support for the flow to * that particular version. * - * Functions that suspend the flow (including all functions on [FlowSession]) accept a [maySkipCheckpoint] parameter + * Functions that suspend the flow (including all functions on [FlowSession]) accept a maySkipCheckpoint parameter * defaulting to false, false meaning a checkpoint should always be created on suspend. This parameter may be set to * true which allows the implementation to potentially optimise away the checkpoint, saving a roundtrip to the database. * @@ -52,6 +52,7 @@ import java.time.Instant * parameter the flow must be prepared for scenarios where a previous running of the flow *already committed its * relevant database transactions*. Only set this option to true if you know what you're doing. */ +@Suppress("DEPRECATION", "DeprecatedCallableAddReplaceWith") abstract class FlowLogic { /** This is where you should log things to. */ val logger: Logger get() = stateMachine.logger @@ -60,14 +61,14 @@ abstract class FlowLogic { /** * Return the outermost [FlowLogic] instance, or null if not in a flow. */ - @JvmStatic + @Suppress("unused") @JvmStatic val currentTopLevel: FlowLogic<*>? get() = (Strand.currentStrand() as? FlowStateMachine<*>)?.logic /** * If on a flow, suspends the flow and only wakes it up after at least [duration] time has passed. Otherwise, * just sleep for [duration]. This sleep function is not designed to aid scheduling, for which you should - * consider using [SchedulableState]. It is designed to aid with managing contention for which you have not - * managed via another means. + * consider using [net.corda.core.contracts.SchedulableState]. It is designed to aid with managing contention + * for which you have not managed via another means. * * Warning: long sleeps and in general long running flows are highly discouraged, as there is currently no * support for flow migration! This method will throw an exception if you attempt to sleep for longer than @@ -77,7 +78,7 @@ abstract class FlowLogic { @JvmStatic @Throws(FlowException::class) fun sleep(duration: Duration) { - if (duration.compareTo(Duration.ofMinutes(5)) > 0) { + if (duration > Duration.ofMinutes(5)) { throw FlowException("Attempt to sleep for longer than 5 minutes is not supported. Consider using SchedulableState.") } (Strand.currentStrand() as? FlowStateMachine<*>)?.sleepUntil(Instant.now() + duration) ?: Strand.sleep(duration.toMillis()) @@ -425,6 +426,7 @@ abstract class FlowLogic { // This is the flow used for managing sessions. It defaults to the current flow but if this is an inlined sub-flow // then it will point to the flow it's been inlined to. + @Suppress("LeakingThis") private var flowUsedForSessions: FlowLogic<*> = this private fun maybeWireUpProgressTracking(subLogic: FlowLogic<*>) { diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt index b6880a8103..5d56f9d638 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -4,7 +4,7 @@ import net.corda.core.concurrent.CordaFuture import net.corda.core.context.Actor import net.corda.core.context.AuthServiceId import net.corda.core.context.InvocationContext -import net.corda.core.context.Origin +import net.corda.core.context.InvocationOrigin import net.corda.core.contracts.ContractState import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowInitiator @@ -27,44 +27,40 @@ import java.io.InputStream import java.security.PublicKey import java.time.Instant -private val unknownName = CordaX500Name("UNKNOWN", "UNKNOWN", "GB") - +/** + * Represents information about a flow (the name "state machine" is legacy, Kotlin users can use the [FlowInfo] type + * alias). You can access progress tracking, information about why the flow was started and so on. + */ @CordaSerializable data class StateMachineInfo @JvmOverloads constructor( + /** A univerally unique ID ([java.util.UUID]) representing this particular instance of the named flow. */ val id: StateMachineRunId, + /** The JVM class name of the flow code. */ val flowLogicClassName: String, - val initiator: FlowInitiator, + /** + * An object representing information about the initiator of the flow. Note that this field is + * superceded by the [invocationContext] property, which has more detail. + */ + @Deprecated("There is more info available using 'context'") val initiator: FlowInitiator, + /** A [DataFeed] of the current progress step as a human readable string, and updates to that string. */ val progressTrackerStepAndUpdates: DataFeed?, - val context: InvocationContext? = null + /** An [InvocationContext] describing why and by whom the flow was started. */ + val invocationContext: InvocationContext = initiator.invocationContext ) { - fun context(): InvocationContext = context ?: contextFrom(initiator) - - private fun contextFrom(initiator: FlowInitiator): InvocationContext { - var actor: Actor? = null - val origin: Origin - when (initiator) { - is FlowInitiator.RPC -> { - actor = Actor(Actor.Id(initiator.username), AuthServiceId("UNKNOWN"), unknownName) - origin = Origin.RPC(actor) - } - is FlowInitiator.Peer -> origin = Origin.Peer(initiator.party.name) - is FlowInitiator.Service -> origin = Origin.Service(initiator.serviceClassName, unknownName) - is FlowInitiator.Shell -> origin = Origin.Shell - is FlowInitiator.Scheduled -> origin = Origin.Scheduled(initiator.scheduledState) - } - return InvocationContext.newInstance(origin = origin, actor = actor) - } - + @Suppress("DEPRECATION") fun copy(id: StateMachineRunId = this.id, flowLogicClassName: String = this.flowLogicClassName, initiator: FlowInitiator = this.initiator, progressTrackerStepAndUpdates: DataFeed? = this.progressTrackerStepAndUpdates): StateMachineInfo { - return copy(id = id, flowLogicClassName = flowLogicClassName, initiator = initiator, progressTrackerStepAndUpdates = progressTrackerStepAndUpdates, context = context) + return copy(id = id, flowLogicClassName = flowLogicClassName, initiator = initiator, progressTrackerStepAndUpdates = progressTrackerStepAndUpdates, invocationContext = invocationContext) } override fun toString(): String = "${javaClass.simpleName}($id, $flowLogicClassName)" } +/** An alias for [StateMachineInfo] which uses more modern terminology. */ +typealias FlowInfo = StateMachineInfo + @CordaSerializable sealed class StateMachineUpdate { abstract val id: StateMachineRunId diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt index b3f7ebbec3..7b8d3b5754 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -3,7 +3,7 @@ package net.corda.node.internal import net.corda.client.rpc.notUsed import net.corda.core.concurrent.CordaFuture import net.corda.core.context.InvocationContext -import net.corda.core.context.Origin +import net.corda.core.context.InvocationOrigin import net.corda.core.contracts.ContractState import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowInitiator @@ -285,11 +285,11 @@ internal class CordaRPCOpsImpl( val principal = origin.principal().name return when (origin) { - is Origin.RPC -> FlowInitiator.RPC(principal) - is Origin.Peer -> services.identityService.wellKnownPartyFromX500Name((origin as Origin.Peer).party)?.let { FlowInitiator.Peer(it) } ?: throw IllegalStateException("Unknown peer with name ${(origin as Origin.Peer).party}.") - is Origin.Service -> FlowInitiator.Service(principal) - is Origin.Shell -> FlowInitiator.Shell - is Origin.Scheduled -> FlowInitiator.Scheduled((origin as Origin.Scheduled).scheduledState) + is InvocationOrigin.RPC -> FlowInitiator.RPC(principal) + is InvocationOrigin.Peer -> services.identityService.wellKnownPartyFromX500Name((origin as InvocationOrigin.Peer).party)?.let { FlowInitiator.Peer(it) } ?: throw IllegalStateException("Unknown peer with name ${(origin as InvocationOrigin.Peer).party}.") + is InvocationOrigin.Service -> FlowInitiator.Service(principal) + is InvocationOrigin.Shell -> FlowInitiator.Shell + is InvocationOrigin.Scheduled -> FlowInitiator.Scheduled((origin as InvocationOrigin.Scheduled).scheduledState) } } diff --git a/node/src/main/kotlin/net/corda/node/services/events/NodeSchedulerService.kt b/node/src/main/kotlin/net/corda/node/services/events/NodeSchedulerService.kt index 72ef0fe6a6..fab75d470c 100644 --- a/node/src/main/kotlin/net/corda/node/services/events/NodeSchedulerService.kt +++ b/node/src/main/kotlin/net/corda/node/services/events/NodeSchedulerService.kt @@ -3,7 +3,7 @@ package net.corda.node.services.events import co.paralleluniverse.fibers.Suspendable import com.google.common.util.concurrent.ListenableFuture import net.corda.core.context.InvocationContext -import net.corda.core.context.Origin +import net.corda.core.context.InvocationOrigin import net.corda.core.contracts.SchedulableState import net.corda.core.contracts.ScheduledActivity import net.corda.core.contracts.ScheduledStateRef @@ -252,7 +252,7 @@ class NodeSchedulerService(private val clock: CordaClock, if (scheduledFlow != null) { flowName = scheduledFlow.javaClass.name // TODO refactor the scheduler to store and propagate the original invocation context - val context = InvocationContext.newInstance(Origin.Scheduled(scheduledState)) + val context = InvocationContext.newInstance(InvocationOrigin.Scheduled(scheduledState)) val future = flowStarter.startFlow(scheduledFlow, context).flatMap { it.resultFuture } future.then { unfinishedSchedules.countDown() diff --git a/node/src/main/kotlin/net/corda/node/shell/FlowWatchPrintingSubscriber.kt b/node/src/main/kotlin/net/corda/node/shell/FlowWatchPrintingSubscriber.kt index 7f52b430f2..cb48f1b829 100644 --- a/node/src/main/kotlin/net/corda/node/shell/FlowWatchPrintingSubscriber.kt +++ b/node/src/main/kotlin/net/corda/node/shell/FlowWatchPrintingSubscriber.kt @@ -72,7 +72,7 @@ class FlowWatchPrintingSubscriber(private val toStream: RenderPrintWriter) : Sub table.add(RowElement().add( LabelElement(formatFlowId(smmUpdate.id)), LabelElement(formatFlowName(smmUpdate.stateMachineInfo.flowLogicClassName)), - LabelElement(formatInvocationContext(smmUpdate.stateMachineInfo.context())), + LabelElement(formatInvocationContext(smmUpdate.stateMachineInfo.invocationContext)), LabelElement("In progress") ).style(stateColor(smmUpdate).fg())) indexMap[smmUpdate.id] = table.rows.size - 1 diff --git a/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt b/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt index b7b47d9165..2842e9bab3 100644 --- a/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt @@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.FlowLogic import net.corda.core.flows.StartableByService import net.corda.core.context.InvocationContext -import net.corda.core.context.Origin +import net.corda.core.context.InvocationOrigin import net.corda.core.node.AppServiceHub import net.corda.core.node.ServiceHub import net.corda.core.node.services.CordaService @@ -45,7 +45,7 @@ class TestCordaService(val appServiceHub: AppServiceHub): SingletonSerializeAsTo fun startServiceFlow() { val handle = appServiceHub.startFlow(DummyServiceFlow()) val context = handle.returnValue.get() - assertEquals(this.javaClass.name, (context.origin as Origin.Service).serviceClassName) + assertEquals(this.javaClass.name, (context.origin as InvocationOrigin.Service).serviceClassName) } fun startServiceFlowAndTrack() { diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index 4ff69ccbb2..f40782a3e7 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -2,7 +2,7 @@ package net.corda.node.services.events import co.paralleluniverse.fibers.Suspendable import net.corda.core.concurrent.CordaFuture -import net.corda.core.context.Origin +import net.corda.core.context.InvocationOrigin import net.corda.core.contracts.* import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FlowLogic @@ -121,7 +121,7 @@ class ScheduledFlowTests { aliceNode.smm.track().updates.subscribe { if (it is StateMachineManager.Change.Add) { val context = it.logic.stateMachine.context - if (context.origin is Origin.Scheduled) + if (context.origin is InvocationOrigin.Scheduled) countScheduledFlows++ } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt index 0ce2359249..85248cab2d 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt @@ -5,7 +5,7 @@ package net.corda.testing.node import net.corda.core.context.Actor import net.corda.core.context.AuthServiceId import net.corda.core.context.InvocationContext -import net.corda.core.context.Origin +import net.corda.core.context.InvocationOrigin import net.corda.core.flows.FlowLogic import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -62,7 +62,7 @@ fun testContext(owningLegalIdentity: CordaX500Name = CordaX500Name("Test Company /** * Starts an already constructed flow. Note that you must be on the server thread to call this method. [InvocationContext] - * has origin [Origin.RPC] and actor with id "Only For Testing". + * has origin [InvocationOrigin.RPC] and actor with id "Only For Testing". */ fun StartedNodeServices.startFlow(logic: FlowLogic): FlowStateMachine = startFlow(logic, newContext()).getOrThrow()