Merge pull request #14 from corda/mattb/CORE-8063-review-and-complete-kotlin-to-java-conversion
Mattb/core 8063 review and complete kotlin to java conversion
This commit is contained in:
		
						commit
						3fcf9a1c49
					
				
							
								
								
									
										88
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					# Eclipse, ctags, Mac metadata, log files
 | 
				
			||||||
 | 
					.classpath
 | 
				
			||||||
 | 
					.project
 | 
				
			||||||
 | 
					.settings
 | 
				
			||||||
 | 
					tags
 | 
				
			||||||
 | 
					.DS_Store
 | 
				
			||||||
 | 
					*.log
 | 
				
			||||||
 | 
					*.orig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Created by .ignore support plugin (hsz.mobi)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.gradle
 | 
				
			||||||
 | 
					local.properties
 | 
				
			||||||
 | 
					.gradletasknamecache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# General build files
 | 
				
			||||||
 | 
					**/build/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lib/quasar.jar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**/logs/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### JetBrains template
 | 
				
			||||||
 | 
					# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*.iml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Directory-based project format:
 | 
				
			||||||
 | 
					.idea/*.xml
 | 
				
			||||||
 | 
					.idea/.name
 | 
				
			||||||
 | 
					.idea/copyright
 | 
				
			||||||
 | 
					.idea/inspectionProfiles
 | 
				
			||||||
 | 
					.idea/libraries
 | 
				
			||||||
 | 
					.idea/shelf
 | 
				
			||||||
 | 
					.idea/dataSources
 | 
				
			||||||
 | 
					.idea/markdown-navigator
 | 
				
			||||||
 | 
					.idea/runConfigurations
 | 
				
			||||||
 | 
					.idea/dictionaries
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Include the -parameters compiler option by default in IntelliJ required for serialization.
 | 
				
			||||||
 | 
					!.idea/codeStyleSettings.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## File-based project format:
 | 
				
			||||||
 | 
					*.ipr
 | 
				
			||||||
 | 
					*.iws
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Plugin-specific files:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# IntelliJ
 | 
				
			||||||
 | 
					**/out/
 | 
				
			||||||
 | 
					/classes/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# vim
 | 
				
			||||||
 | 
					*.swp
 | 
				
			||||||
 | 
					*.swn
 | 
				
			||||||
 | 
					*.swo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Directory generated during Resolve and TestOSGi gradle tasks
 | 
				
			||||||
 | 
					bnd/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Ignore Gradle build output directory
 | 
				
			||||||
 | 
					build
 | 
				
			||||||
 | 
					/.idea/codeStyles/codeStyleConfig.xml
 | 
				
			||||||
 | 
					/.idea/codeStyles/Project.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Ignore Visual studio directory
 | 
				
			||||||
 | 
					bin/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*.cpi
 | 
				
			||||||
 | 
					*.cpb
 | 
				
			||||||
 | 
					*.cpk
 | 
				
			||||||
 | 
					workspace/**
 | 
				
			||||||
 | 
					#CordaPID.dat
 | 
				
			||||||
 | 
					#*.pem
 | 
				
			||||||
 | 
					#*.pfx
 | 
				
			||||||
 | 
					#CPIFileStatus*.json
 | 
				
			||||||
 | 
					#GroupPolicy.json
 | 
				
			||||||
							
								
								
									
										8
									
								
								.idea/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					# Default ignored files
 | 
				
			||||||
 | 
					/shelf/
 | 
				
			||||||
 | 
					/workspace.xml
 | 
				
			||||||
 | 
					# Editor-based HTTP Client requests
 | 
				
			||||||
 | 
					/httpRequests/
 | 
				
			||||||
 | 
					# Datasource local storage ignored files
 | 
				
			||||||
 | 
					/dataSources/
 | 
				
			||||||
 | 
					/dataSources.local.xml
 | 
				
			||||||
@ -2,19 +2,15 @@ import static org.gradle.api.JavaVersion.VERSION_11
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
plugins {
 | 
					plugins {
 | 
				
			||||||
    id 'org.jetbrains.kotlin.jvm'
 | 
					    id 'org.jetbrains.kotlin.jvm'
 | 
				
			||||||
 | 
					 | 
				
			||||||
    id 'net.corda.cordapp.cordapp-configuration'
 | 
					    id 'net.corda.cordapp.cordapp-configuration'
 | 
				
			||||||
 | 
					 | 
				
			||||||
    id 'org.jetbrains.kotlin.plugin.jpa'
 | 
					    id 'org.jetbrains.kotlin.plugin.jpa'
 | 
				
			||||||
 | 
					 | 
				
			||||||
    id 'java'
 | 
					    id 'java'
 | 
				
			||||||
    id 'maven-publish'
 | 
					    id 'maven-publish'
 | 
				
			||||||
 | 
					 | 
				
			||||||
    id 'csde'
 | 
					    id 'csde'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
allprojects {
 | 
					allprojects {
 | 
				
			||||||
    group 'com.r3.hellocorda'
 | 
					    group 'com.r3.developers.csdetemplate'
 | 
				
			||||||
    version '1.0-SNAPSHOT'
 | 
					    version '1.0-SNAPSHOT'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def javaVersion = VERSION_11
 | 
					    def javaVersion = VERSION_11
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,7 @@
 | 
				
			|||||||
// todo: add test corda running/live task
 | 
					// todo: add test corda running/live task
 | 
				
			||||||
// todo: add a test to check docker is running and display error if not + halt start corda
 | 
					// todo: add a test to check docker is running and display error if not + halt start corda
 | 
				
			||||||
// todo: add a clean corda task.
 | 
					// todo: add a clean corda task.
 | 
				
			||||||
 | 
					// todo: fix logging level and make it configurable.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.r3.csde.CordaLifeCycleHelper
 | 
					import com.r3.csde.CordaLifeCycleHelper
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					package com.r3.developers.csdetemplate.utxoexample.contracts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.r3.developers.csdetemplate.utxoexample.states.ChatState;
 | 
				
			||||||
 | 
					import net.corda.v5.base.exceptions.CordaRuntimeException;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.Command;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.Contract;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.ContractState;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.StateAndRef;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction;
 | 
				
			||||||
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.security.PublicKey;
 | 
				
			||||||
 | 
					import java.util.Objects;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static java.util.Objects.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// todo: START here
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ChatContract implements Contract {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final static Logger log = LoggerFactory.getLogger(ChatContract.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static class Create implements Command { }
 | 
				
			||||||
 | 
					    public static class Update implements Command { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//    @Override
 | 
				
			||||||
 | 
					//    public boolean isRelevant(@NotNull ContractState state, @NotNull Set<? extends PublicKey> myKeys) {
 | 
				
			||||||
 | 
					//        return Contract.super.isRelevant(state, myKeys);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void verify(UtxoLedgerTransaction transaction) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//        Command command = requireNonNull( transaction.getCommands().get(0), "Require a single command");  // this doesn't ensure there is one command
 | 
				
			||||||
 | 
					        requireThat( transaction.getCommands().size() == 1, "Require a single command.");
 | 
				
			||||||
 | 
					        Command command = transaction.getCommands().get(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ChatState output = transaction.getOutputStates(ChatState.class).get(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        requireThat(output.getParticipants().size() == 2, "The output state should have two and only two participants.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(command.getClass() == Create.class) {
 | 
				
			||||||
 | 
					            requireThat(transaction.getInputContractStates().isEmpty(), "When command is Create there should be no input states.");
 | 
				
			||||||
 | 
					            requireThat(transaction.getOutputContractStates().size() == 1, "When command is Create there should be one and only one output state.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if(command.getClass() == Update.class) {
 | 
				
			||||||
 | 
					            requireThat(transaction.getInputContractStates().size() == 1, "When command is Update there should be one and only one input state.");
 | 
				
			||||||
 | 
					            requireThat(transaction.getOutputContractStates().size() == 1, "When command is Update there should be one and only one output state.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ChatState input = transaction.getInputStates(ChatState.class).get(0);
 | 
				
			||||||
 | 
					            requireThat(input.getId().equals(output.getId()), "When command is Update id must not change.");
 | 
				
			||||||
 | 
					            requireThat(input.getChatName().equals(output.getChatName()), "When command is Update chatName must not change.");
 | 
				
			||||||
 | 
					            requireThat(
 | 
				
			||||||
 | 
					                    input.getParticipants().containsAll(output.getParticipants()) &&
 | 
				
			||||||
 | 
					                    output.getParticipants().containsAll(input.getParticipants()),
 | 
				
			||||||
 | 
					                    "When command is Update participants must not change.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            throw new CordaRuntimeException("Unsupported command");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void requireThat(boolean asserted, String errorMessage) {
 | 
				
			||||||
 | 
					        if(!asserted) {
 | 
				
			||||||
 | 
					            throw new CordaRuntimeException("Failed requirement: " + errorMessage);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					package com.r3.developers.csdetemplate.utxoexample.states;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.r3.developers.csdetemplate.utxoexample.contracts.ChatContract;
 | 
				
			||||||
 | 
					import net.corda.v5.base.annotations.ConstructorForDeserialization;
 | 
				
			||||||
 | 
					import net.corda.v5.base.annotations.CordaSerializable;
 | 
				
			||||||
 | 
					import net.corda.v5.base.types.MemberX500Name;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.BelongsToContract;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.ContractState;
 | 
				
			||||||
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.security.PublicKey;
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// todo: Clear out commented code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//@CordaSerializable
 | 
				
			||||||
 | 
					@BelongsToContract(ChatContract.class)
 | 
				
			||||||
 | 
					public class ChatState implements ContractState {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private UUID id;
 | 
				
			||||||
 | 
					    private String chatName;
 | 
				
			||||||
 | 
					    private MemberX500Name messageFrom;
 | 
				
			||||||
 | 
					    private String message;
 | 
				
			||||||
 | 
					    public List<PublicKey> participants;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//    public ChatState() {        // todo why do we need this?
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Allows serialisation and to use a specified UUID.
 | 
				
			||||||
 | 
					    @ConstructorForDeserialization
 | 
				
			||||||
 | 
					    public ChatState(UUID id,
 | 
				
			||||||
 | 
					                     String chatName,
 | 
				
			||||||
 | 
					                     MemberX500Name messageFrom,
 | 
				
			||||||
 | 
					                     String message,
 | 
				
			||||||
 | 
					                     List<PublicKey> participants) {
 | 
				
			||||||
 | 
					        this.id = id;
 | 
				
			||||||
 | 
					        this.chatName = chatName;
 | 
				
			||||||
 | 
					        this.messageFrom = messageFrom;
 | 
				
			||||||
 | 
					        this.message = message;
 | 
				
			||||||
 | 
					        this.participants = participants;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//    // Convenience constructor for initial ChatState objects that need a new UUID generated.
 | 
				
			||||||
 | 
					//    public ChatState(String chatName,
 | 
				
			||||||
 | 
					//                     MemberX500Name messageFrom,
 | 
				
			||||||
 | 
					//                     String message,
 | 
				
			||||||
 | 
					//                     List<PublicKey> participants) {
 | 
				
			||||||
 | 
					//        this(UUID.randomUUID(),
 | 
				
			||||||
 | 
					//                chatName,
 | 
				
			||||||
 | 
					//                messageFrom,
 | 
				
			||||||
 | 
					//                message,
 | 
				
			||||||
 | 
					//                participants);
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public UUID getId() {
 | 
				
			||||||
 | 
					        return id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public String getChatName() {
 | 
				
			||||||
 | 
					        return chatName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public MemberX500Name getMessageFrom() {
 | 
				
			||||||
 | 
					        return messageFrom;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public String getMessage() {
 | 
				
			||||||
 | 
					        return message;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					//    @NotNull
 | 
				
			||||||
 | 
					//    @Override
 | 
				
			||||||
 | 
					    public List<PublicKey> getParticipants() {
 | 
				
			||||||
 | 
					        return participants;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ChatState updateMessage(MemberX500Name name, String message) {
 | 
				
			||||||
 | 
					        return new ChatState(id, chatName, name, message, participants);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//    // todo: why is this overridden
 | 
				
			||||||
 | 
					//    @Override
 | 
				
			||||||
 | 
					//    public String toString() {
 | 
				
			||||||
 | 
					//        return ChatState.class.getName() +
 | 
				
			||||||
 | 
					//                "(id=" + id +
 | 
				
			||||||
 | 
					//                ", chatName=" + chatName +
 | 
				
			||||||
 | 
					//                ", messageFrom=" + messageFrom +
 | 
				
			||||||
 | 
					//                ", message=" + message +
 | 
				
			||||||
 | 
					//                ", participants=" + participants +
 | 
				
			||||||
 | 
					//                ")";
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -22,6 +22,3 @@ rootProject.name = 'csde-cordapp-template-java'
 | 
				
			|||||||
include ':workflows'
 | 
					include ':workflows'
 | 
				
			||||||
include ':contracts'
 | 
					include ':contracts'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
package com.r3.developers.csdetemplate.workflows;
 | 
					package com.r3.developers.csdetemplate.flowexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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;
 | 
				
			||||||
@ -7,6 +7,10 @@ import net.corda.v5.base.types.MemberX500Name;
 | 
				
			|||||||
// send it from one virtual node to another.
 | 
					// send it from one virtual node to another.
 | 
				
			||||||
@CordaSerializable
 | 
					@CordaSerializable
 | 
				
			||||||
public class Message {
 | 
					public class Message {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private MemberX500Name sender;
 | 
				
			||||||
 | 
					    private String 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;
 | 
				
			||||||
@ -20,6 +24,5 @@ public class Message {
 | 
				
			|||||||
        return message;
 | 
					        return message;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MemberX500Name sender;
 | 
					
 | 
				
			||||||
    public String message;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
package com.r3.developers.csdetemplate.workflows;
 | 
					package com.r3.developers.csdetemplate.flowexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import net.corda.v5.application.flows.*;
 | 
					import net.corda.v5.application.flows.*;
 | 
				
			||||||
import net.corda.v5.application.marshalling.JsonMarshallingService;
 | 
					import net.corda.v5.application.marshalling.JsonMarshallingService;
 | 
				
			||||||
@ -19,7 +19,7 @@ import org.slf4j.LoggerFactory;
 | 
				
			|||||||
public class MyFirstFlow implements RPCStartableFlow {
 | 
					public class MyFirstFlow implements RPCStartableFlow {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Log messages from the flows for debugging.
 | 
					    // Log messages from the flows for debugging.
 | 
				
			||||||
    private final Logger log = LoggerFactory.getLogger(MyFirstFlow.class);
 | 
					    private final static 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.
 | 
				
			||||||
@ -58,7 +58,7 @@ public class MyFirstFlow implements RPCStartableFlow {
 | 
				
			|||||||
        MyFirstFlowStartArgs flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, MyFirstFlowStartArgs.class);
 | 
					        MyFirstFlowStartArgs flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, MyFirstFlowStartArgs.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Obtain the MemberX500Name of the counterparty.
 | 
					        // Obtain the MemberX500Name of the counterparty.
 | 
				
			||||||
        MemberX500Name otherMember = flowArgs.otherMember;
 | 
					        MemberX500Name otherMember = flowArgs.getOtherMember();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Get our identity from the MemberLookup service.
 | 
					        // Get our identity from the MemberLookup service.
 | 
				
			||||||
        MemberX500Name ourIdentity = memberLookup.myInfo().getName();
 | 
					        MemberX500Name ourIdentity = memberLookup.myInfo().getName();
 | 
				
			||||||
@ -67,7 +67,7 @@ public class MyFirstFlow implements RPCStartableFlow {
 | 
				
			|||||||
        Message message = new Message(otherMember, "Hello from " + ourIdentity + ".");
 | 
					        Message message = new Message(otherMember, "Hello from " + ourIdentity + ".");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 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.getMessage());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 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.
 | 
				
			||||||
@ -82,16 +82,15 @@ public class MyFirstFlow implements RPCStartableFlow {
 | 
				
			|||||||
        // The return value of a RPCStartableFlow must always be a String. This 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.getMessage();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
RequestBody for triggering the flow via http-rpc:
 | 
					RequestBody for triggering the flow via http-rpc:
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    "clientRequestId": "r1",
 | 
					    "clientRequestId": "r1",
 | 
				
			||||||
    "flowClassName": "com.r3.developers.csdetemplate.workflows.MyFirstFlow",
 | 
					    "flowClassName": "com.r3.developers.csdetemplate.flowexample.workflows.MyFirstFlow",
 | 
				
			||||||
    "requestData": {
 | 
					    "requestData": {
 | 
				
			||||||
        "otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB"
 | 
					        "otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB"
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
package com.r3.developers.csdetemplate.workflows;
 | 
					package com.r3.developers.csdetemplate.flowexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import net.corda.v5.application.flows.CordaInject;
 | 
					import net.corda.v5.application.flows.CordaInject;
 | 
				
			||||||
import net.corda.v5.application.flows.InitiatedBy;
 | 
					import net.corda.v5.application.flows.InitiatedBy;
 | 
				
			||||||
@ -18,7 +18,7 @@ import org.slf4j.LoggerFactory;
 | 
				
			|||||||
public class MyFirstFlowResponder implements ResponderFlow {
 | 
					public class MyFirstFlowResponder implements ResponderFlow {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Log messages from the flows for debugging.
 | 
					    // Log messages from the flows for debugging.
 | 
				
			||||||
    private final Logger log = LoggerFactory.getLogger(MyFirstFlowResponder.class);
 | 
					    private final static Logger log = LoggerFactory.getLogger(MyFirstFlowResponder.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // MemberLookup looks for information about members of the virtual network which 
 | 
					    // MemberLookup looks for information about members of the virtual network which 
 | 
				
			||||||
    // this CorDapp operates in. 
 | 
					    // this CorDapp operates in. 
 | 
				
			||||||
@ -44,7 +44,7 @@ public class MyFirstFlowResponder implements ResponderFlow {
 | 
				
			|||||||
        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.
 | 
				
			||||||
        log.info("MFF: Message received from " + receivedMessage.sender + ":" + receivedMessage.message);
 | 
					        log.info("MFF: Message received from " + receivedMessage.getSender() + ":" + receivedMessage.getMessage());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Get our identity from the MemberLookup service.
 | 
					        // Get our identity from the MemberLookup service.
 | 
				
			||||||
        MemberX500Name ourIdentity = memberLookup.myInfo().getName();
 | 
					        MemberX500Name ourIdentity = memberLookup.myInfo().getName();
 | 
				
			||||||
@ -54,19 +54,9 @@ public class MyFirstFlowResponder implements ResponderFlow {
 | 
				
			|||||||
                "Hello " + session.getCounterparty().getCommonName() + ", best wishes from " + ourIdentity.getCommonName());
 | 
					                "Hello " + session.getCounterparty().getCommonName() + ", best wishes from " + ourIdentity.getCommonName());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Log the response to be sent.
 | 
					        // Log the response to be sent.
 | 
				
			||||||
        log.info("MFF: response.message: " + response.message);
 | 
					        log.info("MFF: response.message: " + response.getMessage());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Send the response via the send method on the flow session
 | 
					        // Send the response via the send method on the flow session
 | 
				
			||||||
        session.send(response);
 | 
					        session.send(response);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
RequestBody for triggering the flow via http-rpc:
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    "clientRequestId": "r1",
 | 
					 | 
				
			||||||
    "flowClassName": "com.r3.developers.csdetemplate.MyFirstFlow",
 | 
					 | 
				
			||||||
    "requestData": {
 | 
					 | 
				
			||||||
        "otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
@ -1,15 +1,19 @@
 | 
				
			|||||||
package com.r3.developers.csdetemplate.workflows;
 | 
					package com.r3.developers.csdetemplate.flowexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import net.corda.v5.base.types.MemberX500Name;
 | 
					import net.corda.v5.base.types.MemberX500Name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// A class to hold the arguments required to start the flow
 | 
					// A class to hold the arguments required to start the flow
 | 
				
			||||||
public class MyFirstFlowStartArgs {
 | 
					public class MyFirstFlowStartArgs {
 | 
				
			||||||
    public MemberX500Name otherMember;
 | 
					    private MemberX500Name otherMember;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // The JSON Marshalling Service, which handles serialisation, needs this constructor.
 | 
				
			||||||
 | 
					    public MyFirstFlowStartArgs() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public MyFirstFlowStartArgs(MemberX500Name otherMember) {
 | 
					    public MyFirstFlowStartArgs(MemberX500Name otherMember) {
 | 
				
			||||||
        this.otherMember = otherMember;
 | 
					        this.otherMember = otherMember;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // The JSON Marshalling Service, which handles serialisation, needs this constructor.
 | 
					    public MemberX500Name getOtherMember() {
 | 
				
			||||||
    public MyFirstFlowStartArgs() {}
 | 
					        return otherMember;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					package com.r3.developers.csdetemplate.utxoexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.UUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ChatStateResults {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private UUID id;
 | 
				
			||||||
 | 
					    private String chatName;
 | 
				
			||||||
 | 
					    private String messageFromName;
 | 
				
			||||||
 | 
					    private String message;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ChatStateResults() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ChatStateResults(UUID id, String chatName, String messageFromName, String message) {
 | 
				
			||||||
 | 
					        this.id = id;
 | 
				
			||||||
 | 
					        this.chatName = chatName;
 | 
				
			||||||
 | 
					        this.messageFromName = messageFromName;
 | 
				
			||||||
 | 
					        this.message = message;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public UUID getId() {
 | 
				
			||||||
 | 
					        return id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getChatName() {
 | 
				
			||||||
 | 
					        return chatName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getMessageFromName() {
 | 
				
			||||||
 | 
					        return messageFromName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getMessage() {
 | 
				
			||||||
 | 
					        return message;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					package com.r3.developers.csdetemplate.utxoexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.r3.developers.csdetemplate.utxoexample.contracts.ChatContract;
 | 
				
			||||||
 | 
					import com.r3.developers.csdetemplate.utxoexample.states.ChatState;
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.*;
 | 
				
			||||||
 | 
					import net.corda.v5.application.marshalling.JsonMarshallingService;
 | 
				
			||||||
 | 
					import net.corda.v5.application.membership.MemberLookup;
 | 
				
			||||||
 | 
					import net.corda.v5.base.annotations.Suspendable;
 | 
				
			||||||
 | 
					import net.corda.v5.base.exceptions.CordaRuntimeException;
 | 
				
			||||||
 | 
					import net.corda.v5.base.types.MemberX500Name;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.common.NotaryLookup;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.common.Party;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.UtxoLedgerService;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.transaction.UtxoTransactionBuilder;
 | 
				
			||||||
 | 
					import net.corda.v5.membership.MemberInfo;
 | 
				
			||||||
 | 
					import net.corda.v5.membership.NotaryInfo;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.security.PublicKey;
 | 
				
			||||||
 | 
					import java.time.Duration;
 | 
				
			||||||
 | 
					import java.time.Instant;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					import java.util.Objects;
 | 
				
			||||||
 | 
					import java.util.UUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static java.util.Objects.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class CreateNewChatFlow implements RPCStartableFlow {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final static Logger log = LoggerFactory.getLogger(CreateNewChatFlow.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public JsonMarshallingService jsonMarshallingService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public MemberLookup memberLookup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public UtxoLedgerService ledgerService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public NotaryLookup notaryLookup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public FlowEngine flowEngine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Suspendable
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String call( RPCRequestData requestBody) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        log.info("CreateNewChatFlow.call() called");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            CreateNewChatFlowArgs flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, CreateNewChatFlowArgs.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            MemberInfo myInfo = memberLookup.myInfo();
 | 
				
			||||||
 | 
					            MemberInfo otherMember = requireNonNull(
 | 
				
			||||||
 | 
					                    memberLookup.lookup(MemberX500Name.parse(flowArgs.getOtherMember())),
 | 
				
			||||||
 | 
					                    "MemberLookup can't find otherMember specified in flow arguments."
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ChatState chatState = new ChatState(
 | 
				
			||||||
 | 
					                    UUID.randomUUID(),
 | 
				
			||||||
 | 
					                    flowArgs.getChatName(),
 | 
				
			||||||
 | 
					                    myInfo.getName(),
 | 
				
			||||||
 | 
					                    flowArgs.getMessage(),
 | 
				
			||||||
 | 
					                    Arrays.asList(myInfo.getLedgerKeys().get(0), otherMember.getLedgerKeys().get(0))
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            NotaryInfo notary = notaryLookup.getNotaryServices().iterator().next();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            PublicKey notaryKey = null;
 | 
				
			||||||
 | 
					            for(MemberInfo memberInfo: memberLookup.lookup()){
 | 
				
			||||||
 | 
					                if(Objects.equals(
 | 
				
			||||||
 | 
					                        memberInfo.getMemberProvidedContext().get("corda.notary.service.name"),
 | 
				
			||||||
 | 
					                        notary.getName().toString())) {
 | 
				
			||||||
 | 
					                    notaryKey = memberInfo.getLedgerKeys().get(0);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if(notaryKey == null) {
 | 
				
			||||||
 | 
					                throw new CordaRuntimeException("No notary PublicKey found");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            UtxoTransactionBuilder txBuilder = ledgerService.getTransactionBuilder()
 | 
				
			||||||
 | 
					                    .setNotary(new Party(notary.getName(), notaryKey))
 | 
				
			||||||
 | 
					                    .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
 | 
				
			||||||
 | 
					                    .addOutputState(chatState)
 | 
				
			||||||
 | 
					                    .addCommand(new ChatContract.Create())
 | 
				
			||||||
 | 
					                    .addSignatories(chatState.getParticipants());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @SuppressWarnings("DEPRECATION")
 | 
				
			||||||
 | 
					            UtxoSignedTransaction signedTransaction = txBuilder.toSignedTransaction(myInfo.getLedgerKeys().get(0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return flowEngine.subFlow(new FinalizeChatSubFlow(signedTransaction, otherMember.getName()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (Exception e) {
 | 
				
			||||||
 | 
					            log.warn("Failed to process utxo flow for request body " + requestBody + " because: " + e.getMessage());
 | 
				
			||||||
 | 
					            throw new CordaRuntimeException(e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					RequestBody for triggering the flow via http-rpc:
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "clientRequestId": "create-1",
 | 
				
			||||||
 | 
					    "flowClassName": "com.r3.developers.csdetemplate.utxoexample.workflows.CreateNewChatFlow",
 | 
				
			||||||
 | 
					    "requestData": {
 | 
				
			||||||
 | 
					        "chatName":"Chat with Bob",
 | 
				
			||||||
 | 
					        "otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB",
 | 
				
			||||||
 | 
					        "message": "Hello Bob"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					package com.r3.developers.csdetemplate.utxoexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class CreateNewChatFlowArgs{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Serialisation service requires a default constructor
 | 
				
			||||||
 | 
					    public CreateNewChatFlowArgs() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String chatName;
 | 
				
			||||||
 | 
					    private String message;
 | 
				
			||||||
 | 
					    private String otherMember;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public CreateNewChatFlowArgs(String chatName, String message, String otherMember) {
 | 
				
			||||||
 | 
					        this.chatName = chatName;
 | 
				
			||||||
 | 
					        this.message = message;
 | 
				
			||||||
 | 
					        this.otherMember = otherMember;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getChatName() {
 | 
				
			||||||
 | 
					        return chatName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getMessage() {
 | 
				
			||||||
 | 
					        return message;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getOtherMember() {
 | 
				
			||||||
 | 
					        return otherMember;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					package com.r3.developers.csdetemplate.utxoexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.r3.developers.csdetemplate.utxoexample.states.ChatState;
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.CordaInject;
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.InitiatedBy;
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.ResponderFlow;
 | 
				
			||||||
 | 
					import net.corda.v5.application.messaging.FlowSession;
 | 
				
			||||||
 | 
					import net.corda.v5.base.annotations.Suspendable;
 | 
				
			||||||
 | 
					import net.corda.v5.base.types.MemberX500Name;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.UtxoLedgerService;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.transaction.UtxoTransactionValidator;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@InitiatedBy(protocol = "finalize-chat-protocol")
 | 
				
			||||||
 | 
					public class FinalizeChatResponderFlow implements ResponderFlow {
 | 
				
			||||||
 | 
					    private final static Logger log = LoggerFactory.getLogger(FinalizeChatResponderFlow.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public UtxoLedgerService utxoLedgerService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Suspendable
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void call(FlowSession session) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        log.info("FinalizeChatResponderFlow.call() called");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            UtxoTransactionValidator txValidator = ledgerTransaction -> {
 | 
				
			||||||
 | 
					                ChatState state = (ChatState) ledgerTransaction.getOutputContractStates().get(0);
 | 
				
			||||||
 | 
					                if (checkForBannedWords(state.getMessage()) || !checkMessageFromMatchesCounterparty(state, session.getCounterparty())) {
 | 
				
			||||||
 | 
					                    throw new IllegalStateException("Failed verification");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                log.info("Verified the transaction - " + ledgerTransaction.getId());
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            UtxoSignedTransaction finalizedSignedTransaction = utxoLedgerService.receiveFinality(session, txValidator);
 | 
				
			||||||
 | 
					            log.info("Finished responder flow - " + finalizedSignedTransaction.getId());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch(Exception e)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            log.warn("Exceptionally finished responder flow", e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Suspendable
 | 
				
			||||||
 | 
					    Boolean checkForBannedWords(String str) {
 | 
				
			||||||
 | 
					        List<String> bannedWords = Arrays.asList("banana", "apple", "pear");
 | 
				
			||||||
 | 
					        return bannedWords.stream().anyMatch(str::contains);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Suspendable
 | 
				
			||||||
 | 
					    Boolean checkMessageFromMatchesCounterparty(ChatState state, MemberX500Name otherMember) {
 | 
				
			||||||
 | 
					        return state.getMessageFrom().equals(otherMember);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					package com.r3.developers.csdetemplate.utxoexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.*;
 | 
				
			||||||
 | 
					import net.corda.v5.application.messaging.FlowMessaging;
 | 
				
			||||||
 | 
					import net.corda.v5.application.messaging.FlowSession;
 | 
				
			||||||
 | 
					import net.corda.v5.base.annotations.Suspendable;
 | 
				
			||||||
 | 
					import net.corda.v5.base.types.MemberX500Name;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.UtxoLedgerService;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@InitiatingFlow(protocol = "finalize-chat-protocol")
 | 
				
			||||||
 | 
					public class FinalizeChatSubFlow implements SubFlow<String> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final static Logger log = LoggerFactory.getLogger(FinalizeChatSubFlow.class);
 | 
				
			||||||
 | 
					    private final UtxoSignedTransaction signedTransaction;
 | 
				
			||||||
 | 
					    private final MemberX500Name otherMember;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public FinalizeChatSubFlow(UtxoSignedTransaction signedTransaction, MemberX500Name otherMember) {
 | 
				
			||||||
 | 
					        this.signedTransaction = signedTransaction;
 | 
				
			||||||
 | 
					        this.otherMember = otherMember;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public UtxoLedgerService ledgerService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public FlowMessaging flowMessaging;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    @Suspendable
 | 
				
			||||||
 | 
					    public String call() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        log.info("FinalizeChatFlow.call() called");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        FlowSession session = flowMessaging.initiateFlow(otherMember);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String result;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            List<FlowSession> sessionsList = Arrays.asList(session);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            UtxoSignedTransaction finalizedSignedTransaction = ledgerService.finalize(
 | 
				
			||||||
 | 
					                    signedTransaction,
 | 
				
			||||||
 | 
					                    sessionsList
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            result = finalizedSignedTransaction.getId().toString();
 | 
				
			||||||
 | 
					            log.info("Success! Response: " + result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            log.warn("Finality failed", e);
 | 
				
			||||||
 | 
					            result = "Finality failed, " + e.getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,100 @@
 | 
				
			|||||||
 | 
					package com.r3.developers.csdetemplate.utxoexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.r3.developers.csdetemplate.utxoexample.states.ChatState;
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.CordaInject;
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.RPCRequestData;
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.RPCStartableFlow;
 | 
				
			||||||
 | 
					import net.corda.v5.application.marshalling.JsonMarshallingService;
 | 
				
			||||||
 | 
					import net.corda.v5.base.annotations.Suspendable;
 | 
				
			||||||
 | 
					import net.corda.v5.base.exceptions.CordaRuntimeException;
 | 
				
			||||||
 | 
					import net.corda.v5.crypto.SecureHash;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.StateAndRef;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.UtxoLedgerService;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction;
 | 
				
			||||||
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					import static java.util.Objects.*;
 | 
				
			||||||
 | 
					import static java.util.stream.Collectors.toList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class GetChatFlow implements RPCStartableFlow {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final static Logger log = LoggerFactory.getLogger(GetChatFlow.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public JsonMarshallingService jsonMarshallingService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public UtxoLedgerService ledgerService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//    @NotNull
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    @Suspendable
 | 
				
			||||||
 | 
					    public String call(RPCRequestData requestBody)  {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        GetChatFlowArgs flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, GetChatFlowArgs.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        List<StateAndRef<ChatState>> chatStateAndRefs = ledgerService.findUnconsumedStatesByType(ChatState.class);
 | 
				
			||||||
 | 
					        List<StateAndRef<ChatState>> chatStateAndRefsWithId = chatStateAndRefs.stream()
 | 
				
			||||||
 | 
					                .filter(sar -> sar.getState().getContractState().getId().equals(flowArgs.getId())).collect(toList());
 | 
				
			||||||
 | 
					        if (chatStateAndRefsWithId.size() != 1) throw new CordaRuntimeException("Multiple or zero Chat states with id " + flowArgs.getId() + " found");
 | 
				
			||||||
 | 
					        StateAndRef<ChatState> chatStateAndRef = chatStateAndRefsWithId.get(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return jsonMarshallingService.format(resolveMessagesFromBackchain(chatStateAndRef, flowArgs.getNumberOfRecords() ));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NotNull
 | 
				
			||||||
 | 
					    @Suspendable
 | 
				
			||||||
 | 
					    private List<GetChatResponse> resolveMessagesFromBackchain(StateAndRef<?> stateAndRef, int numberOfRecords) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        List<GetChatResponse> messages = new LinkedList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        StateAndRef<?> currentStateAndRef = stateAndRef;
 | 
				
			||||||
 | 
					        int recordsToFetch = numberOfRecords;
 | 
				
			||||||
 | 
					        boolean moreBackchain = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while (moreBackchain) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            SecureHash transactionId = currentStateAndRef.getRef().getTransactionHash();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            UtxoLedgerTransaction transaction = requireNonNull(
 | 
				
			||||||
 | 
					                 ledgerService.findLedgerTransaction(transactionId),
 | 
				
			||||||
 | 
					                 "Transaction " +  transactionId + " not found."
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            List<ChatState> chatStates = transaction.getOutputStates(ChatState.class);
 | 
				
			||||||
 | 
					            if (chatStates.size() != 1) throw new CordaRuntimeException(
 | 
				
			||||||
 | 
					                    "Expecting one and only one ChatState output for transaction " + transactionId + ".");
 | 
				
			||||||
 | 
					            ChatState output = chatStates.get(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            messages.add(new GetChatResponse(output.getMessageFrom().toString(), output.getMessage()));
 | 
				
			||||||
 | 
					            recordsToFetch--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            List<StateAndRef<?>> inputStateAndRefs = transaction.getInputStateAndRefs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						        if (inputStateAndRefs.isEmpty() || recordsToFetch == 0) {
 | 
				
			||||||
 | 
						            moreBackchain = false;
 | 
				
			||||||
 | 
						        } else if (inputStateAndRefs.size() > 1) {
 | 
				
			||||||
 | 
						            throw new CordaRuntimeException("More than one input state found for transaction " + transactionId + ".");
 | 
				
			||||||
 | 
						        } else {
 | 
				
			||||||
 | 
						            currentStateAndRef = inputStateAndRefs.get(0);
 | 
				
			||||||
 | 
						        }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return messages;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					RequestBody for triggering the flow via http-rpc:
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "clientRequestId": "get-1",
 | 
				
			||||||
 | 
					    "flowClassName": "com.r3.developers.csdetemplate.utxoexample.workflows.GetChatFlow",
 | 
				
			||||||
 | 
					    "requestData": {
 | 
				
			||||||
 | 
					        "id":"** fill in id **",
 | 
				
			||||||
 | 
					        "numberOfRecords":"4"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					package com.r3.developers.csdetemplate.utxoexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.UUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class GetChatFlowArgs {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private UUID id;
 | 
				
			||||||
 | 
					    private int numberOfRecords;
 | 
				
			||||||
 | 
					    public GetChatFlowArgs() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public GetChatFlowArgs(UUID id, int numberOfRecords ) {
 | 
				
			||||||
 | 
					        this.id = id;
 | 
				
			||||||
 | 
					        this.numberOfRecords = numberOfRecords;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public UUID getId() {
 | 
				
			||||||
 | 
					        return id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getNumberOfRecords() {
 | 
				
			||||||
 | 
					        return numberOfRecords;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					package com.r3.developers.csdetemplate.utxoexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class GetChatResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String messageFrom;
 | 
				
			||||||
 | 
					    private String message;
 | 
				
			||||||
 | 
					    public GetChatResponse() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public GetChatResponse(String messageFrom, String message) {
 | 
				
			||||||
 | 
					        this.messageFrom = messageFrom;
 | 
				
			||||||
 | 
					        this.message = message;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getMessageFrom() {
 | 
				
			||||||
 | 
					        return messageFrom;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getMessage() {
 | 
				
			||||||
 | 
					        return message;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					package com.r3.developers.csdetemplate.utxoexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.r3.developers.csdetemplate.utxoexample.states.ChatState;
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.CordaInject;
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.RPCRequestData;
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.RPCStartableFlow;
 | 
				
			||||||
 | 
					import net.corda.v5.application.marshalling.JsonMarshallingService;
 | 
				
			||||||
 | 
					import net.corda.v5.base.annotations.Suspendable;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.StateAndRef;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.UtxoLedgerService;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ListChatsFlow implements RPCStartableFlow{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final static Logger log = LoggerFactory.getLogger(ListChatsFlow.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public JsonMarshallingService jsonMarshallingService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public UtxoLedgerService utxoLedgerService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Suspendable
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String call(RPCRequestData requestBody) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        log.info("ListChatsFlow.call() called");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        List<StateAndRef<ChatState>> states = utxoLedgerService.findUnconsumedStatesByType(ChatState.class);
 | 
				
			||||||
 | 
					        List<ChatStateResults> results = states.stream().map( stateAndRef ->
 | 
				
			||||||
 | 
					            new ChatStateResults(
 | 
				
			||||||
 | 
					                    stateAndRef.getState().getContractState().getId(),
 | 
				
			||||||
 | 
					                    stateAndRef.getState().getContractState().getChatName(),
 | 
				
			||||||
 | 
					                    stateAndRef.getState().getContractState().getMessageFrom().toString(),
 | 
				
			||||||
 | 
					                    stateAndRef.getState().getContractState().getMessage()
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					        ).collect(Collectors.toList());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return jsonMarshallingService.format(results);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					RequestBody for triggering the flow via http-rpc:
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "clientRequestId": "list-1",
 | 
				
			||||||
 | 
					    "flowClassName": "com.r3.developers.csdetemplate.utxoexample.workflows.ListChatsFlow",
 | 
				
			||||||
 | 
					    "requestData": {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
@ -0,0 +1,103 @@
 | 
				
			|||||||
 | 
					package com.r3.developers.csdetemplate.utxoexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.r3.developers.csdetemplate.utxoexample.contracts.ChatContract;
 | 
				
			||||||
 | 
					import com.r3.developers.csdetemplate.utxoexample.states.ChatState;
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.CordaInject;
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.FlowEngine;
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.RPCRequestData;
 | 
				
			||||||
 | 
					import net.corda.v5.application.flows.RPCStartableFlow;
 | 
				
			||||||
 | 
					import net.corda.v5.application.marshalling.JsonMarshallingService;
 | 
				
			||||||
 | 
					import net.corda.v5.application.membership.MemberLookup;
 | 
				
			||||||
 | 
					import net.corda.v5.base.annotations.Suspendable;
 | 
				
			||||||
 | 
					import net.corda.v5.base.exceptions.CordaRuntimeException;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.StateAndRef;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.UtxoLedgerService;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction;
 | 
				
			||||||
 | 
					import net.corda.v5.ledger.utxo.transaction.UtxoTransactionBuilder;
 | 
				
			||||||
 | 
					import net.corda.v5.membership.MemberInfo;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.time.Duration;
 | 
				
			||||||
 | 
					import java.time.Instant;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static java.util.Objects.*;
 | 
				
			||||||
 | 
					import static java.util.stream.Collectors.toList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class UpdateChatFlow implements RPCStartableFlow {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final static Logger log = LoggerFactory.getLogger(UpdateChatFlow.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public JsonMarshallingService jsonMarshallingService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public MemberLookup memberLookup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public UtxoLedgerService ledgerService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @CordaInject
 | 
				
			||||||
 | 
					    public FlowEngine flowEngine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Suspendable
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String call(RPCRequestData requestBody) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        log.info("UpdateNewChatFlow.call() called");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					             UpdateChatFlowArgs flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, UpdateChatFlowArgs.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            List<StateAndRef<ChatState>> chatStateAndRefs = ledgerService.findUnconsumedStatesByType(ChatState.class);
 | 
				
			||||||
 | 
					            List<StateAndRef<ChatState>> chatStateAndRefsWithId = chatStateAndRefs.stream()
 | 
				
			||||||
 | 
					                    .filter(sar -> sar.getState().getContractState().getId().equals(flowArgs.getId())).collect(toList());
 | 
				
			||||||
 | 
					            if (chatStateAndRefsWithId.size() != 1) throw new CordaRuntimeException("Multiple or zero Chat states with id " + flowArgs.getId() + " found");
 | 
				
			||||||
 | 
					            StateAndRef<ChatState> chatStateAndRef = chatStateAndRefsWithId.get(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            MemberInfo myInfo = memberLookup.myInfo();
 | 
				
			||||||
 | 
					            ChatState state = chatStateAndRef.getState().getContractState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            List<MemberInfo> members = state.getParticipants().stream().map(
 | 
				
			||||||
 | 
					                    it -> requireNonNull(memberLookup.lookup(it), "Member not found from public Key "+ it + ".")
 | 
				
			||||||
 | 
					            ).collect(toList());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            members.remove(myInfo);
 | 
				
			||||||
 | 
					            if(members.size() != 1) throw new RuntimeException("Should be only one participant other than the initiator");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            MemberInfo otherMember = members.get(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ChatState newChatState = state.updateMessage(myInfo.getName(), flowArgs.getMessage());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            UtxoTransactionBuilder txBuilder = ledgerService.getTransactionBuilder()
 | 
				
			||||||
 | 
					                    .setNotary(chatStateAndRef.getState().getNotary())
 | 
				
			||||||
 | 
					                    .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
 | 
				
			||||||
 | 
					                    .addOutputState(newChatState)
 | 
				
			||||||
 | 
					                    .addInputState(chatStateAndRef.getRef())
 | 
				
			||||||
 | 
					                    .addCommand(new ChatContract.Update())
 | 
				
			||||||
 | 
					                    .addSignatories(newChatState.getParticipants());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @SuppressWarnings("DEPRECATION")
 | 
				
			||||||
 | 
					            UtxoSignedTransaction signedTransaction = txBuilder.toSignedTransaction(myInfo.getLedgerKeys().get(0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return flowEngine.subFlow(new FinalizeChatSubFlow(signedTransaction, otherMember.getName()));
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            log.warn("Failed to process utxo flow for request body " + requestBody + " because: " + e.getMessage());
 | 
				
			||||||
 | 
					            throw e;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					RequestBody for triggering the flow via http-rpc:
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "clientRequestId": "update-1",
 | 
				
			||||||
 | 
					    "flowClassName": "com.r3.developers.csdetemplate.utxoexample.workflows.UpdateChatFlow",
 | 
				
			||||||
 | 
					    "requestData": {
 | 
				
			||||||
 | 
					        "id":" ** fill in id **",
 | 
				
			||||||
 | 
					        "message": "How are you today?"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					package com.r3.developers.csdetemplate.utxoexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.UUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class UpdateChatFlowArgs {
 | 
				
			||||||
 | 
					    public UpdateChatFlowArgs() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private UUID id;
 | 
				
			||||||
 | 
					    private String message;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public UpdateChatFlowArgs(UUID id, String message) {
 | 
				
			||||||
 | 
					        this.id = id;
 | 
				
			||||||
 | 
					        this.message = message;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public UUID getId() {
 | 
				
			||||||
 | 
					        return id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getMessage() {
 | 
				
			||||||
 | 
					        return message;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
package com.r3.developers.csdetemplate.workflows;
 | 
					package com.r3.developers.csdetemplate.flowexample.workflows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import net.corda.simulator.RequestData;
 | 
					import net.corda.simulator.RequestData;
 | 
				
			||||||
import net.corda.simulator.SimulatedVirtualNode;
 | 
					import net.corda.simulator.SimulatedVirtualNode;
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user