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:
Ross Nicoll 2018-01-04 13:32:10 +00:00 committed by GitHub
parent 8a3299fa2a
commit 412fead02e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 37 additions and 11 deletions

View File

@ -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<*>
}

View File

@ -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.

View File

@ -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

View File

@ -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)
}
}

View File

@ -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

View File

@ -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))
}