From 7fb3791c80976f93a4c63c8d818aaaa9a0ad0ac8 Mon Sep 17 00:00:00 2001 From: mattbradburyr3 Date: Mon, 30 Jan 2023 08:18:12 +0000 Subject: [PATCH] Added commentary --- .../workflows/ChatStateResults.java | 5 +++ .../workflows/CreateNewChatFlow.java | 18 +++++++++-- .../workflows/CreateNewChatFlowArgs.java | 1 + .../workflows/FinalizeChatResponderFlow.java | 15 ++++++++- .../workflows/FinalizeChatSubFlow.java | 14 ++++++-- .../utxoexample/workflows/GetChatFlow.java | 32 +++++++++++++++---- .../workflows/GetChatFlowArgs.java | 1 + .../utxoexample/workflows/ListChatsFlow.java | 4 +++ ...hatResponse.java => MessageAndSender.java} | 7 ++-- .../utxoexample/workflows/UpdateChatFlow.java | 25 ++++++++++++--- .../workflows/UpdateChatFlowArgs.java | 1 + 11 files changed, 105 insertions(+), 18 deletions(-) rename workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/{GetChatResponse.java => MessageAndSender.java} (64%) diff --git a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/ChatStateResults.java b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/ChatStateResults.java index 9a6b500..5c3f37f 100644 --- a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/ChatStateResults.java +++ b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/ChatStateResults.java @@ -2,6 +2,11 @@ package com.r3.developers.csdetemplate.utxoexample.workflows; import java.util.UUID; +// Class to hold the ListChatFlow results. +// The ChatState(s) cannot be returned directly as the JsonMarshallingService can only serialize simple classes +// that the underlying Jackson serializer recognises, hence creating a DTO style object which consists only of Strings +// and a UUID. It is possible to create custom serializers for the JsonMarshallingService, but this beyond the scope +// of this simple example. public class ChatStateResults { private UUID id; diff --git a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/CreateNewChatFlow.java b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/CreateNewChatFlow.java index 2b1de5f..5f6f459 100644 --- a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/CreateNewChatFlow.java +++ b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/CreateNewChatFlow.java @@ -27,6 +27,7 @@ import java.util.UUID; import static java.util.Objects.*; +// See Chat CorDapp Design section of the getting started docs for a description of this flow. public class CreateNewChatFlow implements RPCStartableFlow { private final static Logger log = LoggerFactory.getLogger(CreateNewChatFlow.class); @@ -37,12 +38,14 @@ public class CreateNewChatFlow implements RPCStartableFlow { @CordaInject public MemberLookup memberLookup; + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. @CordaInject public UtxoLedgerService ledgerService; @CordaInject public NotaryLookup notaryLookup; + // FlowEngine service is required to run SubFlows. @CordaInject public FlowEngine flowEngine; @@ -54,14 +57,17 @@ public class CreateNewChatFlow implements RPCStartableFlow { log.info("CreateNewChatFlow.call() called"); try { + // Obtain the deserialized input arguments to the flow from the requestBody. CreateNewChatFlowArgs flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, CreateNewChatFlowArgs.class); + // Get MemberInfos for the Vnode running the flow and the otherMember. MemberInfo myInfo = memberLookup.myInfo(); MemberInfo otherMember = requireNonNull( memberLookup.lookup(MemberX500Name.parse(flowArgs.getOtherMember())), "MemberLookup can't find otherMember specified in flow arguments." ); + // Create the ChatState from the input arguments and member information. ChatState chatState = new ChatState( UUID.randomUUID(), flowArgs.getChatName(), @@ -70,8 +76,8 @@ public class CreateNewChatFlow implements RPCStartableFlow { Arrays.asList(myInfo.getLedgerKeys().get(0), otherMember.getLedgerKeys().get(0)) ); + // Obtain the Notary name and public key. NotaryInfo notary = notaryLookup.getNotaryServices().iterator().next(); - PublicKey notaryKey = null; for(MemberInfo memberInfo: memberLookup.lookup()){ if(Objects.equals( @@ -81,11 +87,13 @@ public class CreateNewChatFlow implements RPCStartableFlow { break; } } - + // Note, in Java CorDapps only unchecked RuntimeExceptions can be thrown not + // declared checked exceptions as this changes the method signature and breaks override. if(notaryKey == null) { throw new CordaRuntimeException("No notary PublicKey found"); } + // Use UTXOTransactionBuilder to build up the draft transaction. UtxoTransactionBuilder txBuilder = ledgerService.getTransactionBuilder() .setNotary(new Party(notary.getName(), notaryKey)) .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) @@ -93,11 +101,17 @@ public class CreateNewChatFlow implements RPCStartableFlow { .addCommand(new ChatContract.Create()) .addSignatories(chatState.getParticipants()); + // Convert the transaction builder to a UTXOSignedTransaction and sign with this Vnode's first Ledger key. + // Note, toSignedTransaction() is currently a placeholder method, hence being marked as deprecated. @SuppressWarnings("DEPRECATION") UtxoSignedTransaction signedTransaction = txBuilder.toSignedTransaction(myInfo.getLedgerKeys().get(0)); + // Call FinalizeChatSubFlow which will finalise the transaction. + // If successful the flow will return a String of the created transaction id, + // if not successful it will return an error message. return flowEngine.subFlow(new FinalizeChatSubFlow(signedTransaction, otherMember.getName())); } + // Catch any exceptions, log them and rethrow the exception. catch (Exception e) { log.warn("Failed to process utxo flow for request body " + requestBody + " because: " + e.getMessage()); throw new CordaRuntimeException(e.getMessage()); diff --git a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/CreateNewChatFlowArgs.java b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/CreateNewChatFlowArgs.java index 56c5b67..c2b815e 100644 --- a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/CreateNewChatFlowArgs.java +++ b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/CreateNewChatFlowArgs.java @@ -1,5 +1,6 @@ package com.r3.developers.csdetemplate.utxoexample.workflows; +// A class to hold the deserialized arguments required to start the flow. public class CreateNewChatFlowArgs{ // Serialisation service requires a default constructor diff --git a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/FinalizeChatResponderFlow.java b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/FinalizeChatResponderFlow.java index 51af173..01f4b31 100644 --- a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/FinalizeChatResponderFlow.java +++ b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/FinalizeChatResponderFlow.java @@ -6,6 +6,7 @@ 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.exceptions.CordaRuntimeException; import net.corda.v5.base.types.MemberX500Name; import net.corda.v5.ledger.utxo.UtxoLedgerService; import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction; @@ -16,10 +17,15 @@ import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.List; + +// See Chat CorDapp Design section of the getting started docs for a description of this flow. + +//@InitiatingBy declares the protocol which will be used to link the initiator to the responder. @InitiatedBy(protocol = "finalize-chat-protocol") public class FinalizeChatResponderFlow implements ResponderFlow { private final static Logger log = LoggerFactory.getLogger(FinalizeChatResponderFlow.class); + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. @CordaInject public UtxoLedgerService utxoLedgerService; @@ -30,17 +36,24 @@ public class FinalizeChatResponderFlow implements ResponderFlow { log.info("FinalizeChatResponderFlow.call() called"); try { + // Defines the lambda validator used in receiveFinality below. UtxoTransactionValidator txValidator = ledgerTransaction -> { ChatState state = (ChatState) ledgerTransaction.getOutputContractStates().get(0); + // Uses checkForBannedWords() and checkMessageFromMatchesCounterparty() functions + // to check whether to sign the transaction. if (checkForBannedWords(state.getMessage()) || !checkMessageFromMatchesCounterparty(state, session.getCounterparty())) { - throw new IllegalStateException("Failed verification"); + throw new CordaRuntimeException("Failed verification"); } log.info("Verified the transaction - " + ledgerTransaction.getId()); }; + // Calls receiveFinality() function which provides the responder to the finalise() function + // in the Initiating Flow. Accepts a lambda validator containing the business logic to decide whether + // responder should sign the Transaction. UtxoSignedTransaction finalizedSignedTransaction = utxoLedgerService.receiveFinality(session, txValidator); log.info("Finished responder flow - " + finalizedSignedTransaction.getId()); } + // Soft fails the flow and log the exception. catch(Exception e) { log.warn("Exceptionally finished responder flow", e); diff --git a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/FinalizeChatSubFlow.java b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/FinalizeChatSubFlow.java index 0148e0f..12f8e98 100644 --- a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/FinalizeChatSubFlow.java +++ b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/FinalizeChatSubFlow.java @@ -13,6 +13,9 @@ import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.List; +// See Chat CorDapp Design section of the getting started docs for a description of this flow. + +// @InitiatingFlow declares the protocol which will be used to link the initiator to the responder. @InitiatingFlow(protocol = "finalize-chat-protocol") public class FinalizeChatSubFlow implements SubFlow { @@ -25,6 +28,7 @@ public class FinalizeChatSubFlow implements SubFlow { this.otherMember = otherMember; } + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. @CordaInject public UtxoLedgerService ledgerService; @@ -37,8 +41,12 @@ public class FinalizeChatSubFlow implements SubFlow { log.info("FinalizeChatFlow.call() called"); + // Initiates a session with the other Member. FlowSession session = flowMessaging.initiateFlow(otherMember); + // Calls the Corda provided finalise() function which gather signatures from the counterparty, + // notarises the transaction and persists the transaction to each party's vault. + // On success returns the id of the transaction created. (This is different to the ChatState id) String result; try { List sessionsList = Arrays.asList(session); @@ -51,11 +59,13 @@ public class FinalizeChatSubFlow implements SubFlow { result = finalizedSignedTransaction.getId().toString(); log.info("Success! Response: " + result); - } catch (Exception e) { + } + // Soft fails the flow and returns the error message without throwing a flow exception. + catch (Exception e) { log.warn("Finality failed", e); result = "Finality failed, " + e.getMessage(); } - + // Returns the transaction id converted as a string return result; } } diff --git a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/GetChatFlow.java b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/GetChatFlow.java index 66d1064..e8ca894 100644 --- a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/GetChatFlow.java +++ b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/GetChatFlow.java @@ -11,7 +11,6 @@ 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; @@ -19,6 +18,7 @@ import java.util.*; import static java.util.Objects.*; import static java.util.stream.Collectors.toList; +// See Chat CorDapp Design section of the getting started docs for a description of this flow. public class GetChatFlow implements RPCStartableFlow { private final static Logger log = LoggerFactory.getLogger(GetChatFlow.class); @@ -26,6 +26,7 @@ public class GetChatFlow implements RPCStartableFlow { @CordaInject public JsonMarshallingService jsonMarshallingService; + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. @CordaInject public UtxoLedgerService ledgerService; @@ -34,46 +35,65 @@ public class GetChatFlow implements RPCStartableFlow { @Suspendable public String call(RPCRequestData requestBody) { + // Obtain the deserialized input arguments to the flow from the requestBody. GetChatFlowArgs flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, GetChatFlowArgs.class); + // Look up the latest unconsumed ChatState with the given id. + // Note, this code brings all unconsumed states back, then filters them. + // This is an inefficient way to perform this operation when there are a large number of chats. + // Note, you will get this error if you input an id which has no corresponding ChatState (common error). List> chatStateAndRefs = ledgerService.findUnconsumedStatesByType(ChatState.class); List> 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 chatStateAndRef = chatStateAndRefsWithId.get(0); + // Calls resolveMessagesFromBackchain() which retrieves the chat history from the backchain. return jsonMarshallingService.format(resolveMessagesFromBackchain(chatStateAndRef, flowArgs.getNumberOfRecords() )); } - @NotNull + // resoveMessageFromBackchain() starts at the stateAndRef provided, which represents the unconsumed head of the + // backchain for this particular chat, then walks the chain backwards for the number of transaction specified in + // the numberOfRecords argument. For each transaction it adds the MessageAndSender representing the + // message and who sent it to a list which is then returned. @Suspendable - private List resolveMessagesFromBackchain(StateAndRef stateAndRef, int numberOfRecords) { + private List resolveMessagesFromBackchain(StateAndRef stateAndRef, int numberOfRecords) { - List messages = new LinkedList<>(); + // Set up a Mutable List to collect the MessageAndSender(s) + List messages = new LinkedList<>(); + // Set up initial conditions for walking the backchain. StateAndRef currentStateAndRef = stateAndRef; int recordsToFetch = numberOfRecords; boolean moreBackchain = true; + // Continue to loop until the start of the backchain or enough records have been retrieved. while (moreBackchain) { + // Obtain the transaction id from the current StateAndRef and fetch the transaction from the vault. SecureHash transactionId = currentStateAndRef.getRef().getTransactionHash(); - UtxoLedgerTransaction transaction = requireNonNull( ledgerService.findLedgerTransaction(transactionId), "Transaction " + transactionId + " not found." ); + // Get the output state from the transaction and use it to create a MessageAndSender Object which + // is appended to the mutable list. List 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())); + messages.add(new MessageAndSender(output.getMessageFrom().toString(), output.getMessage())); + // Decrement the number of records to fetch. recordsToFetch--; + // Get the reference to the input states. List> inputStateAndRefs = transaction.getInputStateAndRefs(); + // Check if there are no more input states (start of chain) or we have retrieved enough records. + // Check the transaction is not malformed by having too many input states. + // Set the currentStateAndRef to the input StateAndRef, then repeat the loop. if (inputStateAndRefs.isEmpty() || recordsToFetch == 0) { moreBackchain = false; } else if (inputStateAndRefs.size() > 1) { diff --git a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/GetChatFlowArgs.java b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/GetChatFlowArgs.java index 05d1934..d1bac81 100644 --- a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/GetChatFlowArgs.java +++ b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/GetChatFlowArgs.java @@ -2,6 +2,7 @@ package com.r3.developers.csdetemplate.utxoexample.workflows; import java.util.UUID; +// A class to hold the deserialized arguments required to start the flow. public class GetChatFlowArgs { private UUID id; diff --git a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/ListChatsFlow.java b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/ListChatsFlow.java index 0e526cc..2f04e35 100644 --- a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/ListChatsFlow.java +++ b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/ListChatsFlow.java @@ -14,6 +14,7 @@ import org.slf4j.LoggerFactory; import java.util.*; import java.util.stream.Collectors; +// See Chat CorDapp Design section of the getting started docs for a description of this flow. public class ListChatsFlow implements RPCStartableFlow{ private final static Logger log = LoggerFactory.getLogger(ListChatsFlow.class); @@ -21,6 +22,7 @@ public class ListChatsFlow implements RPCStartableFlow{ @CordaInject public JsonMarshallingService jsonMarshallingService; + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. @CordaInject public UtxoLedgerService utxoLedgerService; @@ -30,6 +32,7 @@ public class ListChatsFlow implements RPCStartableFlow{ log.info("ListChatsFlow.call() called"); + // Queries the VNode's vault for unconsumed states and converts the result to a serializable DTO. List> states = utxoLedgerService.findUnconsumedStatesByType(ChatState.class); List results = states.stream().map( stateAndRef -> new ChatStateResults( @@ -40,6 +43,7 @@ public class ListChatsFlow implements RPCStartableFlow{ ) ).collect(Collectors.toList()); + // Uses the JsonMarshallingService's format() function to serialize the DTO to Json. return jsonMarshallingService.format(results); } } diff --git a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/GetChatResponse.java b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/MessageAndSender.java similarity index 64% rename from workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/GetChatResponse.java rename to workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/MessageAndSender.java index 4fbedc9..7be54fd 100644 --- a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/GetChatResponse.java +++ b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/MessageAndSender.java @@ -1,12 +1,13 @@ package com.r3.developers.csdetemplate.utxoexample.workflows; -public class GetChatResponse { +// A class to pair the messageFrom and message together. +public class MessageAndSender { private String messageFrom; private String message; - public GetChatResponse() {} + public MessageAndSender() {} - public GetChatResponse(String messageFrom, String message) { + public MessageAndSender(String messageFrom, String message) { this.messageFrom = messageFrom; this.message = message; } diff --git a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/UpdateChatFlow.java b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/UpdateChatFlow.java index 07227b2..e838e2d 100644 --- a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/UpdateChatFlow.java +++ b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/UpdateChatFlow.java @@ -25,6 +25,7 @@ import java.util.List; import static java.util.Objects.*; import static java.util.stream.Collectors.toList; +// See Chat CorDapp Design section of the getting started docs for a description of this flow. public class UpdateChatFlow implements RPCStartableFlow { private final static Logger log = LoggerFactory.getLogger(UpdateChatFlow.class); @@ -35,9 +36,11 @@ public class UpdateChatFlow implements RPCStartableFlow { @CordaInject public MemberLookup memberLookup; + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. @CordaInject public UtxoLedgerService ledgerService; + // FlowEngine service is required to run SubFlows. @CordaInject public FlowEngine flowEngine; @@ -48,29 +51,34 @@ public class UpdateChatFlow implements RPCStartableFlow { log.info("UpdateNewChatFlow.call() called"); try { + // Obtain the deserialized input arguments to the flow from the requestBody. UpdateChatFlowArgs flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, UpdateChatFlowArgs.class); + // Look up the latest unconsumed ChatState with the given id. + // Note, this code brings all unconsumed states back, then filters them. + // This is an inefficient way to perform this operation when there are a large number of chats. + // Note, you will get this error if you input an id which has no corresponding ChatState (common error). List> chatStateAndRefs = ledgerService.findUnconsumedStatesByType(ChatState.class); List> 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 chatStateAndRef = chatStateAndRefsWithId.get(0); - + // Get MemberInfos for the Vnode running the flow and the otherMember. MemberInfo myInfo = memberLookup.myInfo(); ChatState state = chatStateAndRef.getState().getContractState(); List 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); + // Create a new ChatState using the updateMessage helper function. ChatState newChatState = state.updateMessage(myInfo.getName(), flowArgs.getMessage()); + // Use UTXOTransactionBuilder to build up the draft transaction. UtxoTransactionBuilder txBuilder = ledgerService.getTransactionBuilder() .setNotary(chatStateAndRef.getState().getNotary()) .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) @@ -79,11 +87,20 @@ public class UpdateChatFlow implements RPCStartableFlow { .addCommand(new ChatContract.Update()) .addSignatories(newChatState.getParticipants()); + // Convert the transaction builder to a UtxoSignedTransaction and sign with this Vnode's first Ledger key. + // Note, toSignedTransaction() is currently a placeholder method, hence being marked as deprecated. @SuppressWarnings("DEPRECATION") UtxoSignedTransaction signedTransaction = txBuilder.toSignedTransaction(myInfo.getLedgerKeys().get(0)); + // Call FinalizeChatSubFlow which will finalise the transaction. + // If successful the flow will return a String of the created transaction id, + // if not successful it will return an error message. return flowEngine.subFlow(new FinalizeChatSubFlow(signedTransaction, otherMember.getName())); - } catch (Exception e) { + + + } + // Catch any exceptions, log them and rethrow the exception. + catch (Exception e) { log.warn("Failed to process utxo flow for request body " + requestBody + " because: " + e.getMessage()); throw e; } diff --git a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/UpdateChatFlowArgs.java b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/UpdateChatFlowArgs.java index 44d982d..8ac7009 100644 --- a/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/UpdateChatFlowArgs.java +++ b/workflows/src/main/java/com/r3/developers/csdetemplate/utxoexample/workflows/UpdateChatFlowArgs.java @@ -2,6 +2,7 @@ package com.r3.developers.csdetemplate.utxoexample.workflows; import java.util.UUID; +// A class to hold the deserialized arguments required to start the flow. public class UpdateChatFlowArgs { public UpdateChatFlowArgs() {}