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