Merge branch 'main' with Niamh comment updates into chrisbarratt/CORE-8063-update-java-template-with-kotlin-changes
This commit is contained in:
commit
4b12cfca95
4
.ci/Jenkinsfile
vendored
4
.ci/Jenkinsfile
vendored
@ -1,5 +1,5 @@
|
|||||||
@Library('corda-shared-build-pipeline-steps@chrisbarratt/CORE-8075-create-pipeline-for-running-the-autotester') _
|
@Library('corda-shared-build-pipeline-steps@5.0') _
|
||||||
|
|
||||||
cordaPipeline(
|
cordaPipeline(
|
||||||
nexusAppId: 'com.corda.CSDE-Java.5.0',
|
nexusAppId: 'com.corda.CSDE-Java.5.0',
|
||||||
publishRepoPrefix: '',
|
publishRepoPrefix: '',
|
||||||
|
27
build.gradle
27
build.gradle
@ -4,7 +4,7 @@ plugins {
|
|||||||
id 'org.jetbrains.kotlin.jvm'
|
id 'org.jetbrains.kotlin.jvm'
|
||||||
|
|
||||||
// Include the cordapp-cpb plugin. This automatically includes the cordapp-cpk plugin as well.
|
// Include the cordapp-cpb plugin. This automatically includes the cordapp-cpk plugin as well.
|
||||||
// These extend existing build environment so that CPB and CPK files can be built.
|
// These extend the existing build environment so that CPB and CPK files can be built.
|
||||||
// This includes a CorDapp DSL that allows the developer to supply metadata for the CorDapp
|
// This includes a CorDapp DSL that allows the developer to supply metadata for the CorDapp
|
||||||
// required by Corda.
|
// required by Corda.
|
||||||
id 'net.corda.plugins.cordapp-cpb2'
|
id 'net.corda.plugins.cordapp-cpb2'
|
||||||
@ -31,7 +31,7 @@ def javaVersion = VERSION_11
|
|||||||
cordapp {
|
cordapp {
|
||||||
// "targetPlatformVersion" and "minimumPlatformVersion" are intended to specify the preferred
|
// "targetPlatformVersion" and "minimumPlatformVersion" are intended to specify the preferred
|
||||||
// and earliest versions of the Corda platform that the CorDapp will run on respectively.
|
// and earliest versions of the Corda platform that the CorDapp will run on respectively.
|
||||||
// Enforced versioning has not implemented yet so we need to pass in a dummy value for now.
|
// Enforced versioning has not been implemented yet, so enter a dummy value for now.
|
||||||
// The platform version will correspond to and be roughly equivalent to the Corda API version.
|
// The platform version will correspond to and be roughly equivalent to the Corda API version.
|
||||||
targetPlatformVersion platformVersion.toInteger()
|
targetPlatformVersion platformVersion.toInteger()
|
||||||
minimumPlatformVersion platformVersion.toInteger()
|
minimumPlatformVersion platformVersion.toInteger()
|
||||||
@ -64,20 +64,13 @@ repositories {
|
|||||||
|
|
||||||
// Declare dependencies for the modules we will use.
|
// Declare dependencies for the modules we will use.
|
||||||
// A cordaProvided declaration is required for anything that we use that the Corda API provides.
|
// A cordaProvided declaration is required for anything that we use that the Corda API provides.
|
||||||
// This is required to allow us to build CorDapp modules as OSGi bundles that CPI and CPB files are built on.
|
// This is required to allow us to build CorDapp modules as OSGi bundles that CPI and CPB files are then built upon.
|
||||||
dependencies {
|
dependencies {
|
||||||
// We need a version of kotlin-stdlib-jdk8 built as an OSGi bundle, this is "kotlin-stdlib-jdk8-osgi".
|
// Declare a "platform" to use the correct set of dependency versions for the version that the
|
||||||
// R3 builds kotlin-stdlib-jdk8-osgi from Kotlin's kotlin-stdlib-jdk8.
|
// Corda API specifies.
|
||||||
// NB:
|
|
||||||
// Kotlin's kotlin-osgi-bundle does not provide all of the Kotlin API that is required,
|
|
||||||
// There is no kotlin-stdlib-jdk11, but one is not needed even though we are targetting Java 11.
|
|
||||||
cordaProvided 'net.corda.kotlin:kotlin-stdlib-jdk8-osgi'
|
|
||||||
|
|
||||||
// Declare a "platform" so that we use the correct set of dependency versions for the version of the
|
|
||||||
// Corda API specified.
|
|
||||||
cordaProvided platform("net.corda:corda-api:$cordaApiVersion")
|
cordaProvided platform("net.corda:corda-api:$cordaApiVersion")
|
||||||
|
|
||||||
// If using transistive dependencies this will provide most of Corda-API:
|
// If using transistive dependencies this will provide most of the Corda-API:
|
||||||
// cordaProvided 'net.corda:corda-application'
|
// cordaProvided 'net.corda:corda-application'
|
||||||
|
|
||||||
// Alternatively we can explicitly specify all our Corda-API dependencies:
|
// Alternatively we can explicitly specify all our Corda-API dependencies:
|
||||||
@ -95,8 +88,8 @@ dependencies {
|
|||||||
cordaProvided 'org.slf4j:slf4j-api'
|
cordaProvided 'org.slf4j:slf4j-api'
|
||||||
|
|
||||||
// Dependencies Required By Test Tooling
|
// Dependencies Required By Test Tooling
|
||||||
testImplementation "net.corda:corda-simulator-api:$simulatorVersion"
|
testImplementation "net.corda:corda-simulator-api:$combinedWorkerVersion"
|
||||||
testRuntimeOnly "net.corda:corda-simulator-runtime:$simulatorVersion"
|
testRuntimeOnly "net.corda:corda-simulator-runtime:$combinedWorkerVersion"
|
||||||
|
|
||||||
// 3rd party libraries
|
// 3rd party libraries
|
||||||
// Required
|
// Required
|
||||||
@ -104,11 +97,9 @@ dependencies {
|
|||||||
testImplementation "org.junit.jupiter:junit-jupiter:$junitVersion"
|
testImplementation "org.junit.jupiter:junit-jupiter:$junitVersion"
|
||||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||||
|
|
||||||
// Optional but used by exmaple tests.
|
// Optional but used by example tests.
|
||||||
testImplementation "org.mockito:mockito-core:$mockitoVersion"
|
testImplementation "org.mockito:mockito-core:$mockitoVersion"
|
||||||
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion"
|
|
||||||
testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion"
|
testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ def cordaBinDir= System.getProperty('user.home') + "/.corda/corda5"
|
|||||||
def cordaCliBinDir = System.getProperty('user.home') + "/.corda/cli"
|
def cordaCliBinDir = System.getProperty('user.home') + "/.corda/cli"
|
||||||
def cordaJDBCDir = cordaBinDir + "/jdbcDrivers"
|
def cordaJDBCDir = cordaBinDir + "/jdbcDrivers"
|
||||||
def signingCertAlias="gradle-plugin-default-key"
|
def signingCertAlias="gradle-plugin-default-key"
|
||||||
// Get error if this is not a autotyped object
|
// You will receive an error if this is not a autotyped object.
|
||||||
// def signingCertFName = "$rootDir/config/gradle-plugin-default-key.pem"
|
// def signingCertFName = "$rootDir/config/gradle-plugin-default-key.pem".
|
||||||
def signingCertFName = rootDir.toString() + "/config/gradle-plugin-default-key.pem"
|
def signingCertFName = rootDir.toString() + "/config/gradle-plugin-default-key.pem"
|
||||||
def keystoreAlias = "my-signing-key"
|
def keystoreAlias = "my-signing-key"
|
||||||
def keystoreFName = devEnvWorkspace + "/signingkeys.pfx"
|
def keystoreFName = devEnvWorkspace + "/signingkeys.pfx"
|
||||||
@ -136,7 +136,7 @@ tasks.register('createKeystore') {
|
|||||||
"-storetype", "pkcs12",
|
"-storetype", "pkcs12",
|
||||||
"-validity", "4000"
|
"-validity", "4000"
|
||||||
}
|
}
|
||||||
// Need to add the default signing key to the keystore
|
// Add the default signing key to the keystore.
|
||||||
exec {
|
exec {
|
||||||
commandLine "${System.getProperty("java.home")}/bin/keytool", "-importcert",
|
commandLine "${System.getProperty("java.home")}/bin/keytool", "-importcert",
|
||||||
"-keystore", keystoreFName,
|
"-keystore", keystoreFName,
|
||||||
@ -172,7 +172,7 @@ tasks.register('buildCPI') {
|
|||||||
File srcDir
|
File srcDir
|
||||||
srcDir = file('build/libs')
|
srcDir = file('build/libs')
|
||||||
|
|
||||||
// Create a file collection using a closure
|
// Create a file collection using a closure.
|
||||||
def collection = layout.files { srcDir.listFiles() }
|
def collection = layout.files { srcDir.listFiles() }
|
||||||
def cpbs = collection.filter { it.getName().endsWith(".cpb") }
|
def cpbs = collection.filter { it.getName().endsWith(".cpb") }
|
||||||
|
|
||||||
@ -225,7 +225,7 @@ tasks.register('listCPIs') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty task, just acts as the Task user entry point task.
|
// Empty task, this acts as the user entry point task.
|
||||||
tasks.register('deployCordapp') {
|
tasks.register('deployCordapp') {
|
||||||
group = pluginGroupName
|
group = pluginGroupName
|
||||||
dependsOn("createAndRegVNodes")
|
dependsOn("createAndRegVNodes")
|
||||||
|
@ -202,9 +202,9 @@ public class CsdeRpcInterface {
|
|||||||
private boolean uploadStatusRetry(kong.unirest.HttpResponse<kong.unirest.JsonNode> response) {
|
private boolean uploadStatusRetry(kong.unirest.HttpResponse<kong.unirest.JsonNode> response) {
|
||||||
int status = response.getStatus();
|
int status = response.getStatus();
|
||||||
kong.unirest.JsonNode body = response.getBody();
|
kong.unirest.JsonNode body = response.getBody();
|
||||||
// Do not retry on success
|
// Do not retry if successful.
|
||||||
if(status == 200) {
|
if(status == 200) {
|
||||||
// Keep retrying until we get "OK" may move through "Validateing upload", "Persisting CPI"
|
// Retry until you get an "OK"; we may see several other responses before the "OK" response.
|
||||||
return !(body.getObject().get("status").equals("OK"));
|
return !(body.getObject().get("status").equals("OK"));
|
||||||
}
|
}
|
||||||
else if (status == 400){
|
else if (status == 400){
|
||||||
@ -269,7 +269,7 @@ public class CsdeRpcInterface {
|
|||||||
out.println("Upload Status:" + status);
|
out.println("Upload Status:" + status);
|
||||||
out.println("Pretty print the body\n" + body.toPrettyString());
|
out.println("Pretty print the body\n" + body.toPrettyString());
|
||||||
|
|
||||||
// We expect the id field to be a string.
|
// Expect the ID field to be a string.
|
||||||
if (status == 200) {
|
if (status == 200) {
|
||||||
String id = (String) body.getObject().get("id");
|
String id = (String) body.getObject().get("id");
|
||||||
out.println("get id:\n" + id);
|
out.println("get id:\n" + id);
|
||||||
@ -299,7 +299,7 @@ public class CsdeRpcInterface {
|
|||||||
LinkedList<String> x500Ids = getConfigX500Ids();
|
LinkedList<String> x500Ids = getConfigX500Ids();
|
||||||
LinkedList<String> OKHoldingShortIds = new LinkedList<>();
|
LinkedList<String> OKHoldingShortIds = new LinkedList<>();
|
||||||
|
|
||||||
// For each identity check that it already exists.
|
// Create a list of X500 IDs we will not need to create VNodes for.
|
||||||
Set<MemberX500Name> existingX500 = new HashSet<>();
|
Set<MemberX500Name> existingX500 = new HashSet<>();
|
||||||
kong.unirest.HttpResponse<kong.unirest.JsonNode> vnodeListResponse = getVNodeInfo();
|
kong.unirest.HttpResponse<kong.unirest.JsonNode> vnodeListResponse = getVNodeInfo();
|
||||||
|
|
||||||
@ -322,9 +322,9 @@ public class CsdeRpcInterface {
|
|||||||
.asJson();
|
.asJson();
|
||||||
// Logging.
|
// Logging.
|
||||||
|
|
||||||
// need to check this and report errors.
|
// Check this and report errors.
|
||||||
// 200 - OK
|
// 200 - OK.
|
||||||
// 409 - Vnode already exists
|
// 409 - Vnode already exists.
|
||||||
if (jsonNode.getStatus() != 409) {
|
if (jsonNode.getStatus() != 409) {
|
||||||
if (jsonNode.getStatus() != 200) {
|
if (jsonNode.getStatus() != 200) {
|
||||||
reportError(jsonNode);
|
reportError(jsonNode);
|
||||||
@ -340,7 +340,7 @@ public class CsdeRpcInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the VNodes
|
// Register the VNodes.
|
||||||
for(String shortHoldingIdHash: OKHoldingShortIds) {
|
for(String shortHoldingIdHash: OKHoldingShortIds) {
|
||||||
kong.unirest.HttpResponse<kong.unirest.JsonNode> vnodeResponse = Unirest.post(baseURL + "/api/v1/membership/" + shortHoldingIdHash)
|
kong.unirest.HttpResponse<kong.unirest.JsonNode> vnodeResponse = Unirest.post(baseURL + "/api/v1/membership/" + shortHoldingIdHash)
|
||||||
.body("{ \"memberRegistrationRequest\": { \"action\": \"requestJoin\", \"context\": { \"corda.key.scheme\" : \"CORDA.ECDSA.SECP256R1\" } } }")
|
.body("{ \"memberRegistrationRequest\": { \"action\": \"requestJoin\", \"context\": { \"corda.key.scheme\" : \"CORDA.ECDSA.SECP256R1\" } } }")
|
||||||
|
@ -35,4 +35,4 @@ devEnvWorkspace=workspace
|
|||||||
dbContainerName=CSDEpostgresql
|
dbContainerName=CSDEpostgresql
|
||||||
|
|
||||||
# R3 internal repository
|
# R3 internal repository
|
||||||
artifactoryContextUrl=https://staging.download.corda.net/maven/20ede3c6-29c0-11ed-966d-b7c36748b9f6-Beta1.0-HC00
|
artifactoryContextUrl=https://staging.download.corda.net/maven/20ede3c6-29c0-11ed-966d-b7c36748b9f6-Beta1.0-HC00
|
||||||
|
@ -23,3 +23,5 @@ pluginManagement {
|
|||||||
rootProject.name = 'csde-cordapp-template-java'
|
rootProject.name = 'csde-cordapp-template-java'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,15 +3,15 @@ package com.r3.developers.csdetemplate;
|
|||||||
import net.corda.v5.base.annotations.CordaSerializable;
|
import net.corda.v5.base.annotations.CordaSerializable;
|
||||||
import net.corda.v5.base.types.MemberX500Name;
|
import net.corda.v5.base.types.MemberX500Name;
|
||||||
|
|
||||||
// A class which will contain a message, It must be marked with @CordaSerializable for Corda
|
// Where a class contains a message, mark it with @CordaSerializable to enable Corda to
|
||||||
// to be able to send from one virtual node to another.
|
// send it from one virtual node to another.
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
public class Message {
|
public class Message {
|
||||||
// public Message() {}
|
|
||||||
public Message(MemberX500Name sender, String message) {
|
public Message(MemberX500Name sender, String message) {
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemberX500Name getSender() {
|
public MemberX500Name getSender() {
|
||||||
return sender;
|
return sender;
|
||||||
}
|
}
|
||||||
|
@ -14,50 +14,50 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
// MyFirstFlow is an initiating flow, it's corresponding responder flow is called MyFirstFlowResponder (defined below)
|
// MyFirstFlow is an initiating flow, it's corresponding responder flow is called MyFirstFlowResponder (defined below)
|
||||||
// to link the two sides of the flow together they need to have the same protocol.
|
// to link the two sides of the flow together they need to have the same protocol.
|
||||||
@InitiatingFlow(protocol = "another-flow")
|
@InitiatingFlow(protocol = "my-first-flow")
|
||||||
// MyFirstFlow should inherit from RPCStartableFlow, which tells Corda it can be started via an RPC call
|
// MyFirstFlow should inherit from RPCStartableFlow, which tells Corda it can be started via an RPC call
|
||||||
public class MyFirstFlow implements RPCStartableFlow {
|
public class MyFirstFlow implements RPCStartableFlow {
|
||||||
|
|
||||||
// It is useful to be able to log messages from the flows for debugging.
|
// Log messages from the flows for debugging.
|
||||||
private final Logger log = LoggerFactory.getLogger(MyFirstFlow.class);
|
private final Logger log = LoggerFactory.getLogger(MyFirstFlow.class);
|
||||||
|
|
||||||
// Corda has a set of injectable services which are injected into the flow at runtime.
|
// Corda has a set of injectable services which are injected into the flow at runtime.
|
||||||
// Flows declare them with @CordaInjectable, then the flows have access to their services.
|
// Flows declare them with @CordaInjectable, then the flows have access to their services.
|
||||||
|
|
||||||
// JsonMarshallingService provides a Service for manipulating json
|
// JsonMarshallingService provides a service for manipulating JSON.
|
||||||
@CordaInject
|
@CordaInject
|
||||||
public JsonMarshallingService jsonMarshallingService;
|
public JsonMarshallingService jsonMarshallingService;
|
||||||
|
|
||||||
// FlowMessaging provides a service for establishing flow sessions between Virtual Nodes and
|
// FlowMessaging provides a service that establishes flow sessions between virtual nodes
|
||||||
// sending and receiving payloads between them
|
// that send and receive payloads between them.
|
||||||
@CordaInject
|
@CordaInject
|
||||||
public FlowMessaging flowMessaging;
|
public FlowMessaging flowMessaging;
|
||||||
|
|
||||||
// MemberLookup provides a service for looking up information about members of the Virtual Network which
|
// MemberLookup provides a service for looking up information about members of the virtual network which
|
||||||
// this CorDapp is operating in.
|
// this CorDapp operates in.
|
||||||
@CordaInject
|
@CordaInject
|
||||||
public MemberLookup memberLookup;
|
public MemberLookup memberLookup;
|
||||||
|
|
||||||
public MyFirstFlow() {}
|
public MyFirstFlow() {}
|
||||||
|
|
||||||
// When a flow is invoked it's call() method is called.
|
// When a flow is invoked its call() method is called.
|
||||||
// call() methods must be marked as @Suspendable, this allows Corda to pause mid-execution to wait
|
// Call() methods must be marked as @Suspendable, this allows Corda to pause mid-execution to wait
|
||||||
// for a response from the other flows and services
|
// for a response from the other flows and services.
|
||||||
@NotNull
|
@NotNull
|
||||||
@Suspendable
|
@Suspendable
|
||||||
@Override
|
@Override
|
||||||
public String call(@NotNull RPCRequestData requestBody) {
|
public String call(RPCRequestData requestBody) {
|
||||||
|
|
||||||
// Useful logging to follow what's happening in the console or logs
|
// Follow what happens in the console or logs.
|
||||||
log.info("MFF: MyFirstFlow.call() called");
|
log.info("MFF: MyFirstFlow.call() called");
|
||||||
|
|
||||||
// Show the requestBody in the logs - this can be used to help establish the format for starting a flow on corda
|
// Show the requestBody in the logs - this can be used to help establish the format for starting a flow on Corda.
|
||||||
log.info("MFF: requestBody: " + requestBody.getRequestBody());
|
log.info("MFF: requestBody: " + requestBody.getRequestBody());
|
||||||
|
|
||||||
// Deserialize the Json requestBody into the MyfirstFlowStartArgs class using the JsonSerialisation Service
|
// Deserialize the Json requestBody into the MyfirstFlowStartArgs class using the JsonSerialisation service.
|
||||||
MyFirstFlowStartArgs flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, MyFirstFlowStartArgs.class);
|
MyFirstFlowStartArgs flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, MyFirstFlowStartArgs.class);
|
||||||
|
|
||||||
// Obtain the MemberX500Name of counterparty
|
// Obtain the MemberX500Name of the counterparty.
|
||||||
MemberX500Name otherMember = flowArgs.otherMember;
|
MemberX500Name otherMember = flowArgs.otherMember;
|
||||||
|
|
||||||
// Get our identity from the MemberLookup service.
|
// Get our identity from the MemberLookup service.
|
||||||
@ -69,19 +69,19 @@ public class MyFirstFlow implements RPCStartableFlow {
|
|||||||
// Log the message to be sent.
|
// Log the message to be sent.
|
||||||
log.info("MFF: message.message: " + message.message);
|
log.info("MFF: message.message: " + message.message);
|
||||||
|
|
||||||
// Start a flow session with the otherMember using the FlowMessaging service
|
// Start a flow session with the otherMember using the FlowMessaging service.
|
||||||
// The otherMember's Virtual Node will run the corresponding MyFirstFlowResponder responder flow
|
// The otherMember's virtual node will run the corresponding MyFirstFlowResponder responder flow.
|
||||||
FlowSession session = flowMessaging.initiateFlow(otherMember);
|
FlowSession session = flowMessaging.initiateFlow(otherMember);
|
||||||
|
|
||||||
// Send the Payload using the send method on the session to the MyFirstFlowResponder Responder flow
|
// Send the Payload using the send method on the session to the MyFirstFlowResponder responder flow.
|
||||||
session.send(message);
|
session.send(message);
|
||||||
|
|
||||||
// Receive a response from the Responder flow
|
// Receive a response from the responder flow.
|
||||||
Message response = session.receive(Message.class);
|
Message response = session.receive(Message.class);
|
||||||
|
|
||||||
// The return value of a RPCStartableFlow must always be a String, this string will be passed
|
// The return value of a RPCStartableFlow must always be a String. This will be passed
|
||||||
// back as the REST RPC response when the status of the flow is queried on Corda, or as the return
|
// back as the REST RPC response when the status of the flow is queried on Corda, or as the return
|
||||||
// value from the flow when testing using the Simulator
|
// value from the flow when testing using the simulator.
|
||||||
return response.message;
|
return response.message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,4 +96,4 @@ RequestBody for triggering the flow via http-rpc:
|
|||||||
"otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB"
|
"otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
@ -11,36 +11,36 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
// MyFirstFlowResponder is a responder flow, it's corresponding initiating flow is called MyFirstFlow (defined above)
|
// MyFirstFlowResponder is a responder flow, its corresponding initiating flow is called MyFirstFlow (defined in MyFirstFlow.java)
|
||||||
// to link the two sides of the flow together they need to have the same protocol.
|
// to link the two sides of the flow together they need to have the same protocol.
|
||||||
@InitiatedBy(protocol = "another-flow")
|
@InitiatedBy(protocol = "my-first-flow")
|
||||||
// Responder flows must inherit from ResponderFlow
|
// Responder flows must inherit from ResponderFlow
|
||||||
public class MyFirstFlowResponder implements ResponderFlow {
|
public class MyFirstFlowResponder implements ResponderFlow {
|
||||||
|
|
||||||
// It is useful to be able to log messages from the flows for debugging.
|
// Log messages from the flows for debugging.
|
||||||
private final Logger log = LoggerFactory.getLogger(MyFirstFlowResponder.class);
|
private final Logger log = LoggerFactory.getLogger(MyFirstFlowResponder.class);
|
||||||
|
|
||||||
// MemberLookup provides a service for looking up information about members of the Virtual Network which
|
// MemberLookup looks for information about members of the virtual network which
|
||||||
// this CorDapp is operating in.
|
// this CorDapp operates in.
|
||||||
@CordaInject
|
@CordaInject
|
||||||
public MemberLookup memberLookup;
|
public MemberLookup memberLookup;
|
||||||
|
|
||||||
public MyFirstFlowResponder() {}
|
public MyFirstFlowResponder() {}
|
||||||
|
|
||||||
// Responder flows are invoked when an initiating flow makes a call via a session set up with the Virtual
|
// Responder flows are invoked when an initiating flow makes a call via a session set up with the virtual
|
||||||
// node hosting the Responder flow. When a responder flow is invoked it's call() method is called.
|
// node hosting the responder flow. When a responder flow is invoked its call() method is called.
|
||||||
// call() methods must be marked as @Suspendable, this allows Corda to pause mid-execution to wait
|
// Call() methods must be marked as @Suspendable, this allows Corda to pause mid-execution to wait
|
||||||
// for a response from the other flows and services/
|
// for a response from the other flows and services.
|
||||||
// The Call method has the flow session passed in as a parameter by Corda so the session is available to
|
// The Call method has the flow session passed in as a parameter by Corda so the session is available to
|
||||||
// responder flow code, you don't need to inject the FlowMessaging service.
|
// responder flow code, you don't need to inject the FlowMessaging service.
|
||||||
@Suspendable
|
@Suspendable
|
||||||
@Override
|
@Override
|
||||||
public void call(FlowSession session) {
|
public void call(FlowSession session) {
|
||||||
|
|
||||||
// Useful logging to follow what's happening in the console or logs
|
// Follow what happens in the console or logs.
|
||||||
log.info("MFF: MyFirstResponderFlow.call() called");
|
log.info("MFF: MyFirstResponderFlow.call() called");
|
||||||
|
|
||||||
// Receive the payload and deserialize it into a Message class
|
// Receive the payload and deserialize it into a message class.
|
||||||
Message receivedMessage = session.receive(Message.class);
|
Message receivedMessage = session.receive(Message.class);
|
||||||
|
|
||||||
// Log the message as a proxy for performing some useful operation on it.
|
// Log the message as a proxy for performing some useful operation on it.
|
||||||
@ -49,7 +49,7 @@ public class MyFirstFlowResponder implements ResponderFlow {
|
|||||||
// Get our identity from the MemberLookup service.
|
// Get our identity from the MemberLookup service.
|
||||||
MemberX500Name ourIdentity = memberLookup.myInfo().getName();
|
MemberX500Name ourIdentity = memberLookup.myInfo().getName();
|
||||||
|
|
||||||
// Create a response to greet the sender
|
// Create a message to greet the sender.
|
||||||
Message response = new Message(ourIdentity,
|
Message response = new Message(ourIdentity,
|
||||||
"Hello " + session.getCounterparty().getCommonName() + ", best wishes from " + ourIdentity.getCommonName());
|
"Hello " + session.getCounterparty().getCommonName() + ", best wishes from " + ourIdentity.getCommonName());
|
||||||
|
|
||||||
|
@ -6,15 +6,10 @@ import net.corda.v5.base.types.MemberX500Name;
|
|||||||
public class MyFirstFlowStartArgs {
|
public class MyFirstFlowStartArgs {
|
||||||
public MemberX500Name otherMember;
|
public MemberX500Name otherMember;
|
||||||
|
|
||||||
public MemberX500Name getOtherMember() {
|
|
||||||
return otherMember;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MyFirstFlowStartArgs(MemberX500Name otherMember) {
|
public MyFirstFlowStartArgs(MemberX500Name otherMember) {
|
||||||
this.otherMember = otherMember;
|
this.otherMember = otherMember;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Without the following we get
|
// The JSON Marshalling Service, which handles serialisation, needs this constructor.
|
||||||
// "Cannot construct instance of `com.r3.developers.csdetemplate.MyFirstFlowStartArgs` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)\n at [Source: (String)\"{\"otherMember\":\"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB\"}\"; line: 1, column: 2]"
|
|
||||||
public MyFirstFlowStartArgs() {}
|
public MyFirstFlowStartArgs() {}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.r3.developers.csdetemplate;
|
package com.r3.developers.csdetemplate;
|
||||||
|
|
||||||
|
|
||||||
import net.corda.simulator.HoldingIdentity;
|
import net.corda.simulator.HoldingIdentity;
|
||||||
import net.corda.simulator.RequestData;
|
import net.corda.simulator.RequestData;
|
||||||
import net.corda.simulator.SimulatedVirtualNode;
|
import net.corda.simulator.SimulatedVirtualNode;
|
||||||
@ -16,34 +15,34 @@ class MyFirstFlowTest {
|
|||||||
private MemberX500Name bobX500 = MemberX500Name.parse("CN=Bob, OU=Test Dept, O=R3, L=London, C=GB");
|
private MemberX500Name bobX500 = MemberX500Name.parse("CN=Bob, OU=Test Dept, O=R3, L=London, C=GB");
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public void test_that_MyFirstFLow_returns_correct_message() {
|
public void test_that_MyFirstFLow_returns_correct_message() {
|
||||||
|
// Instantiate an instance of the simulator.
|
||||||
// Instantiate an instance of the Simulator
|
|
||||||
Simulator simulator = new Simulator();
|
Simulator simulator = new Simulator();
|
||||||
|
|
||||||
// Create Alice's and Bob HoldingIDs
|
// Create Alice's and Bob HoldingIDs.
|
||||||
HoldingIdentity aliceHoldingID = HoldingIdentity.Companion.create(aliceX500);
|
HoldingIdentity aliceHoldingID = HoldingIdentity.Companion.create(aliceX500);
|
||||||
HoldingIdentity bobHoldingID = HoldingIdentity.Companion.create(bobX500);
|
HoldingIdentity bobHoldingID = HoldingIdentity.Companion.create(bobX500);
|
||||||
|
|
||||||
// Create Alice and Bob's virtual nodes, including the Class's of the flows which will be registered on each node.
|
// Create Alice's and Bob's virtual nodes, including the classes of the flows which will be registered on each node.
|
||||||
// We don't assign Bob's virtual node to a val because we don't need it for this particular test.
|
// Don't assign Bob's virtual node to a value. You don't need it for this particular test.
|
||||||
SimulatedVirtualNode aliceVN = simulator.createVirtualNode(aliceHoldingID, MyFirstFlow.class);
|
SimulatedVirtualNode aliceVN = simulator.createVirtualNode(aliceHoldingID, MyFirstFlow.class);
|
||||||
simulator.createVirtualNode(bobHoldingID, MyFirstFlowResponder.class);
|
simulator.createVirtualNode(bobHoldingID, MyFirstFlowResponder.class);
|
||||||
|
|
||||||
// Create an instance of the MyFirstFlowStartArgs which contains the request arguments for starting the flow
|
// Create an instance of the MyFirstFlowStartArgs which contains the request arguments for starting the flow.
|
||||||
MyFirstFlowStartArgs myFirstFlowStartArgs = new MyFirstFlowStartArgs(bobX500);
|
MyFirstFlowStartArgs myFirstFlowStartArgs = new MyFirstFlowStartArgs(bobX500);
|
||||||
|
|
||||||
// Create a requestData object
|
// Create a requestData object.
|
||||||
RequestData requestData = RequestData.Companion.create(
|
RequestData requestData = RequestData.Companion.create(
|
||||||
"request no 1", // A unique reference for the instance of the flow request
|
"request no 1", // A unique reference for the instance of the flow request.
|
||||||
MyFirstFlow.class, // The name of the flow class which is to be started
|
MyFirstFlow.class, // The name of the flow class which is to be started.
|
||||||
myFirstFlowStartArgs // The object which contains the start arguments of the flow
|
myFirstFlowStartArgs // The object which contains the start arguments of the flow.
|
||||||
);
|
);
|
||||||
|
|
||||||
// Call the Flow on Alice's virtual node and capture the response from the flow
|
// Call the flow on Alice's virtual node and capture the response.
|
||||||
String flowResponse = aliceVN.callFlow(requestData);
|
String flowResponse = aliceVN.callFlow(requestData);
|
||||||
|
|
||||||
// Check that the flow has returned the expected string
|
// Check that the flow has returned the expected string.
|
||||||
assert(flowResponse.equals("Hello Alice, best wishes from Bob"));
|
assert(flowResponse.equals("Hello Alice, best wishes from Bob"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user