mirror of
https://github.com/corda/corda.git
synced 2025-01-20 03:36:29 +00:00
CORDA-785: Add functions for constructing FlowLogicRef without the class (#2134)
Add functions for constructing `FlowLogicRef` from class name, rather than requiring the class itself. This avoids requiring that schedulable states have access to the scheduled flow to instantiate, but instead can require it only actually scheduling the flow. This reduces the size of the JAR required to validate transactions containing these states.
This commit is contained in:
parent
8a3299fa2a
commit
412fead02e
@ -10,7 +10,17 @@ import net.corda.core.serialization.CordaSerializable
|
||||
*/
|
||||
@DoNotImplement
|
||||
interface FlowLogicRefFactory {
|
||||
/**
|
||||
* Construct a FlowLogicRef. This is intended for cases where the calling code has the relevant class already
|
||||
* and can provide it directly.
|
||||
*/
|
||||
@Deprecated("This should be avoided, and the version which takes a class name used instead to avoid requiring the class on the classpath to deserialize calling code")
|
||||
fun create(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef
|
||||
/**
|
||||
* Construct a FlowLogicRef. This is intended for cases where the calling code does not want to require the flow
|
||||
* class on the classpath for all cases where the calling code is loaded.
|
||||
*/
|
||||
fun create(flowClassName: String, vararg args: Any?): FlowLogicRef
|
||||
fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef
|
||||
fun toFlowLogic(ref: FlowLogicRef): FlowLogic<*>
|
||||
}
|
||||
|
@ -40,6 +40,18 @@ class FlowLogicRefFactoryImpl(private val classloader: ClassLoader) : SingletonS
|
||||
return createForRPC(flowClass, *args)
|
||||
}
|
||||
|
||||
override fun create(flowClassName: String, vararg args: Any?): FlowLogicRef {
|
||||
val flowClass = Class.forName(flowClassName, true, classloader).asSubclass(FlowLogic::class.java)
|
||||
if (flowClass == null) {
|
||||
throw IllegalArgumentException("The class $flowClassName is not a subclass of FlowLogic.")
|
||||
} else {
|
||||
if (!flowClass.isAnnotationPresent(SchedulableFlow::class.java)) {
|
||||
throw IllegalFlowLogicException(flowClass, "because it's not a schedulable flow")
|
||||
}
|
||||
return createForRPC(flowClass, *args)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef {
|
||||
// TODO: This is used via RPC but it's probably better if we pass in argument names and values explicitly
|
||||
// to avoid requiring only a single constructor.
|
||||
|
@ -30,6 +30,7 @@ import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.time.Instant
|
||||
import kotlin.reflect.jvm.jvmName
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ScheduledFlowTests {
|
||||
@ -52,7 +53,7 @@ class ScheduledFlowTests {
|
||||
override val linearId: UniqueIdentifier = UniqueIdentifier()) : SchedulableState, LinearState {
|
||||
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
|
||||
return if (!processed) {
|
||||
val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.java, thisStateRef)
|
||||
val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.jvmName, thisStateRef)
|
||||
ScheduledActivity(logicRef, creationTime)
|
||||
} else {
|
||||
null
|
||||
|
@ -1,17 +1,20 @@
|
||||
package net.corda.node.services.events
|
||||
package net.corda.node.services.statemachine
|
||||
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.IllegalFlowLogicException
|
||||
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
|
||||
import net.corda.core.flows.SchedulableFlow
|
||||
import org.junit.Test
|
||||
import java.time.Duration
|
||||
import kotlin.reflect.jvm.jvmName
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class FlowLogicRefTest {
|
||||
class FlowLogicRefFactoryImplTest {
|
||||
|
||||
data class ParamType1(val value: Int)
|
||||
data class ParamType2(val value: String)
|
||||
|
||||
@Suppress("UNUSED_PARAMETER", "unused") // Things are used via reflection.
|
||||
@SchedulableFlow
|
||||
class KotlinFlowLogic(A: ParamType1, b: ParamType2) : FlowLogic<Unit>() {
|
||||
constructor() : this(ParamType1(1), ParamType2("2"))
|
||||
|
||||
@ -26,6 +29,7 @@ class FlowLogicRefTest {
|
||||
override fun call() = Unit
|
||||
}
|
||||
|
||||
@SchedulableFlow
|
||||
class KotlinNoArgFlowLogic : FlowLogic<Unit>() {
|
||||
override fun call() = Unit
|
||||
}
|
||||
@ -37,18 +41,18 @@ class FlowLogicRefTest {
|
||||
private val flowLogicRefFactory = FlowLogicRefFactoryImpl(FlowLogicRefFactoryImpl::class.java.classLoader)
|
||||
@Test
|
||||
fun `create kotlin no arg`() {
|
||||
flowLogicRefFactory.createForRPC(KotlinNoArgFlowLogic::class.java)
|
||||
flowLogicRefFactory.create(KotlinNoArgFlowLogic::class.jvmName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create kotlin`() {
|
||||
fun `should create kotlin types`() {
|
||||
val args = mapOf(Pair("A", ParamType1(1)), Pair("b", ParamType2("Hello Jack")))
|
||||
flowLogicRefFactory.createKotlin(KotlinFlowLogic::class.java, args)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create primary`() {
|
||||
flowLogicRefFactory.createForRPC(KotlinFlowLogic::class.java, ParamType1(1), ParamType2("Hello Jack"))
|
||||
flowLogicRefFactory.create(KotlinFlowLogic::class.jvmName, ParamType1(1), ParamType2("Hello Jack"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -76,6 +80,6 @@ class FlowLogicRefTest {
|
||||
|
||||
@Test(expected = IllegalFlowLogicException::class)
|
||||
fun `create for non-schedulable flow logic`() {
|
||||
flowLogicRefFactory.create(NonSchedulableFlow::class.java)
|
||||
flowLogicRefFactory.create(NonSchedulableFlow::class.jvmName)
|
||||
}
|
||||
}
|
@ -616,7 +616,7 @@ class InterestRateSwap : Contract {
|
||||
|
||||
// This is perhaps not how we should determine the time point in the business day, but instead expect the schedule to detail some of these aspects
|
||||
val instant = suggestInterestRateAnnouncementTimeWindow(index = nextFixingOf.name, source = floatingLeg.indexSource, date = nextFixingOf.forDay).fromTime!!
|
||||
return ScheduledActivity(flowLogicRefFactory.create(FixingFlow.FixingRoleDecider::class.java, thisStateRef), instant)
|
||||
return ScheduledActivity(flowLogicRefFactory.create("net.corda.irs.flows.FixingFlow\$FixingRoleDecider", thisStateRef), instant)
|
||||
}
|
||||
// DOCEND 1
|
||||
|
||||
|
@ -7,7 +7,6 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.finance.contracts.DealState
|
||||
import net.corda.vega.flows.SimmRevaluation
|
||||
import java.time.LocalDate
|
||||
import java.time.ZoneOffset
|
||||
import java.time.temporal.ChronoUnit
|
||||
@ -32,7 +31,7 @@ data class PortfolioState(val portfolio: List<StateRef>,
|
||||
val valuer: AbstractParty get() = participants[0]
|
||||
|
||||
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity {
|
||||
val flow = flowLogicRefFactory.create(SimmRevaluation.Initiator::class.java, thisStateRef, LocalDate.now())
|
||||
val flow = flowLogicRefFactory.create("net.corda.vega.flows.SimmRevaluation\$Initiator", thisStateRef, LocalDate.now())
|
||||
return ScheduledActivity(flow, LocalDate.now().plus(1, ChronoUnit.DAYS).atStartOfDay().toInstant(ZoneOffset.UTC))
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user