diff --git a/tools/jmeter/src/main/kotlin/net/corda/jmeter/CordaRPCSampler.kt b/tools/jmeter/src/main/kotlin/net/corda/jmeter/CordaRPCSampler.kt new file mode 100644 index 0000000000..b6d385fec1 --- /dev/null +++ b/tools/jmeter/src/main/kotlin/net/corda/jmeter/CordaRPCSampler.kt @@ -0,0 +1,83 @@ +package net.corda.jmeter + +import net.corda.client.rpc.CordaRPCClient +import net.corda.client.rpc.CordaRPCConnection +import net.corda.core.flows.FlowLogic +import net.corda.core.messaging.CordaRPCOps +import net.corda.core.utilities.NetworkHostAndPort +import org.apache.jmeter.config.Argument +import org.apache.jmeter.config.Arguments +import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient +import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext +import org.apache.jmeter.samplers.SampleResult + + +class CordaRPCSampler() : AbstractJavaSamplerClient() { + companion object { + val host = Argument("host", "localhost", "", "The remote network address (hostname or IP address) to connect to for RPC.") + val port = Argument("port", "10000", "", "The remote port to connect to for RPC.") + val username = Argument("username", "corda", "", "The RPC user to connect to connect as.") + val password = Argument("password", "corda_is_awesome", "", "The password for the RPC user.") + val className = Argument("pluginClassName", "", "", "The class name of the implementation of ${CordaRPCSampler.Plugin::class.java}.") + + val allArgs = setOf(host, port, username, password, className) + } + + var rpcClient: CordaRPCClient? = null + var rpcConnection: CordaRPCConnection? = null + var rpcProxy: CordaRPCOps? = null + var plugin: Plugin? = null + + override fun getDefaultParameters(): Arguments { + // Add copies of all args, since they seem to be mutable. + return Arguments().apply { for(arg in allArgs) { addArgument(arg.clone() as Argument) } } + } + + override fun setupTest(context: JavaSamplerContext) { + super.setupTest(context) + rpcClient = CordaRPCClient(NetworkHostAndPort(context.getParameter(host.name), context.getIntParameter(port.name))) + rpcConnection = rpcClient!!.start(context.getParameter(username.name), context.getParameter(password.name)) + rpcProxy = rpcConnection!!.proxy + plugin = Class.forName(context.getParameter(className.name)).newInstance() as Plugin + plugin!!.setupTest(rpcProxy!!, context) + } + + override fun runTest(context: JavaSamplerContext): SampleResult { + val flowInvoke = plugin!!.createFlowInvoke(rpcProxy!!, context) + val result = SampleResult() + result.sampleStart() + val handle = rpcProxy!!.startFlowDynamic(flowInvoke!!.flowLogicClass, *(flowInvoke!!.args)) + result.sampleLabel = handle.id.toString() + result.latencyEnd() + try { + val flowResult = handle.returnValue.get() + result.sampleEnd() + return result.apply { + isSuccessful = true + } + } catch(e: Exception) { + result.sampleEnd() + return result.apply { + isSuccessful = false + } + } + } + + override fun teardownTest(context: JavaSamplerContext) { + plugin!!.teardownTest(rpcProxy!!, context) + plugin = null + rpcProxy = null + rpcConnection!!.close() + rpcConnection = null + rpcClient = null + super.teardownTest(context) + } + + interface Plugin { + fun setupTest(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext) + fun createFlowInvoke(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext): FlowInvoke<*> + fun teardownTest(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext) + } + + class FlowInvoke>(val flowLogicClass: Class, val args: Array) +} \ No newline at end of file diff --git a/tools/jmeter/src/main/kotlin/net/corda/jmeter/Launcher.kt b/tools/jmeter/src/main/kotlin/net/corda/jmeter/Launcher.kt new file mode 100644 index 0000000000..47787db764 --- /dev/null +++ b/tools/jmeter/src/main/kotlin/net/corda/jmeter/Launcher.kt @@ -0,0 +1,13 @@ +package net.corda.jmeter + +import org.apache.jmeter.JMeter + +class Launcher { + companion object { + @JvmStatic + fun main(args: Array) { + val jmeter = JMeter() + jmeter.start(args) + } + } +} \ No newline at end of file diff --git a/tools/jmeter/src/main/kotlin/net/corda/jmeter/TraderDemoPlugins.kt b/tools/jmeter/src/main/kotlin/net/corda/jmeter/TraderDemoPlugins.kt new file mode 100644 index 0000000000..1e91443840 --- /dev/null +++ b/tools/jmeter/src/main/kotlin/net/corda/jmeter/TraderDemoPlugins.kt @@ -0,0 +1,47 @@ +package net.corda.jmeter + +import net.corda.core.identity.Party +import net.corda.core.messaging.CordaRPCOps +import net.corda.core.messaging.startFlow +import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.getOrThrow +import net.corda.finance.DOLLARS +import net.corda.finance.flows.CashIssueFlow +import net.corda.jmeter.CordaRPCSampler.FlowInvoke +import net.corda.testing.DUMMY_BANK_A +import net.corda.testing.DUMMY_BANK_B +import net.corda.testing.contracts.calculateRandomlySizedAmounts +import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext +import java.util.* + + +abstract class AsbtractTraderDemoPlugin : CordaRPCSampler.Plugin { + + lateinit var buyer: Party + lateinit var seller: Party + lateinit var notary: Party + + protected fun getIdentities(rpc: CordaRPCOps) { + buyer = rpc.wellKnownPartyFromX500Name(DUMMY_BANK_A.name) ?: throw IllegalStateException("Don't know ${DUMMY_BANK_A.name}") + seller = rpc.wellKnownPartyFromX500Name(DUMMY_BANK_B.name) ?: throw IllegalStateException("Don't know ${DUMMY_BANK_B.name}") + notary = rpc.notaryIdentities().first() + } + +} + +class CashIssuerPlugin : AsbtractTraderDemoPlugin() { + override fun setupTest(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext) { + getIdentities(rpcProxy) + } + + override fun teardownTest(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext) { + } + + override fun createFlowInvoke(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext): FlowInvoke { + val amount = 1_100_000_000_000.DOLLARS + //val amounts = calculateRandomlySizedAmounts(amount, 3, 10, Random()) + //rpc.startFlow(net.corda.finance.flows::CashIssueFlow, amount, OpaqueBytes.of(1), notary).returnValue.getOrThrow() + return FlowInvoke(CashIssueFlow::class.java, arrayOf(amount, OpaqueBytes.of(1), notary)) + } + +} \ No newline at end of file diff --git a/tools/jmeter/src/main/resources/Java Request.jmx b/tools/jmeter/src/main/resources/Java Request.jmx new file mode 100644 index 0000000000..974d09bfac --- /dev/null +++ b/tools/jmeter/src/main/resources/Java Request.jmx @@ -0,0 +1,177 @@ + + + + + + false + false + + + + + + + + continue + + false + 1000 + + 2 + + 1509455820000 + 1509455820000 + false + + + + + + + + + host + localhost + = + + + port + 10012 + = + + + username + demo + = + + + password + demo + = + + + pluginClassName + net.corda.jmeter.CashIssuerPlugin + = + + + + net.corda.jmeter.CordaRPCSampler + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + + + + + + + + true + + + +