Regarding flow initiation - corda

If I have 3 participants and I need to make sure that the contract can only be initiated by, say PartyA. Should I be including this participant as a variable in the respective flow? Because anyhow we'll be initiating the flow from that participant's node right!?

You can hardcode the value inside the Initiator flow:
if (getOurIdentity().equals(CordaX500Name.parse("O=Initiator,L=London,C=GB"))) {
}
But the above step is not enough, the resopnders to your flow don't know (and don't trust) whether you made sure that the initiator is a certain entity or not. So, inside the Responder you must also check the initiator:
if (otherPartySession.getCounterparty()
.equals(CordaX500Name.parse("O=Initiator,L=London,C=GB"))) {
}
Btw, a better option than hardcoding the value, would be to use CorDapp configuration files; read about it in my article here.
On a side note, remember that it's the responsibility of the responding node to implement (i.e. write) a responder flow; just because you gave some node your CorDapp (so they can run the responder), doesn't mean they'll use it; they can create their own version of the responder flow (read here). That's why the check must happen in both Initiator and Responder flows.

Related

In SCORM 2004 (4th ed), why does a choice navigation to a cluster activity change the Current Activity?

The SCORM 2004 4th Edition pseudocode handles the case for a choice request (SB.2.9, steps 12 onward) like so:
If the target activity is a leaf activity Then
Exit Choice Sequencing Request Process (Delivery Request: the target activity; Exception: n/a)
End If
Apply the Flow Subprocess to the target activity in the Forward direction with consider children equal to True
// The identified activity is a cluster. Enter the cluster and attempt to find a descendent leaf to deliver.
If the Flow Subprocess returns False Then
// Nothing to deliver, but we succeeded in reaching the target activity - move the current activity.
Apply the Terminate Descendent Attempts Process to the common ancestor
Apply the End Attempt Process to the common ancestor
Set the Current Activity to the target activity
Exit Choice Sequencing Request Process (Delivery Request: n/a; Exception: SB.2.9-9)
// Nothing to deliver.
Else
Exit Choice Sequencing Request Process (Delivery Request: for the activity identified by the Flow Subprocess; Exception: n/a)
End If
It looks like this means that if the target activity resolves to a cluster activity but the Flow Subprocess can not find any available descendent leaf activity, the Current Activity is still modified and the sequencing request "succeeds" despite returning an exception.
What is the expected behavior for the LMS in this scenario? A cluster activity can't be delivered but this terminates the previous activity. Should the LMS simply deliver a blank page instead of an activity and hope the learner will be available to navigate away to another activity using the navigation controls?
The definition of the Overall Sequencing Process helpfully doesn't specify how an exception is supposed to be handled, but considering that this behavior sets the Current Activity and all successive requests will reference that one instead of the previously active activity, clearly something needs to happen or the LMS will be stuck in an inconsistent state.
Your reading of the pseudocode is correct. Choice is a bit special compared to the other flow events, but the steps of termination and showing the user a "please select an activity from the activity tree" screen could happen in several cases. The only somewhat unique part is the setting of the current activity, which makes it so other flow navigation events the user may select start from their last intentional choice, and not from whatever was previously loaded. It's not unusual for the Current Activity to be on a cluster, as stated on SN-4-18: "During termination behavior, Sequencing Exit Action rules are evaluated on all of the ancestors of the current activity – this is done in the Sequencing Exit Action Rule Subprocess. The result of this subprocess will be that either the “just terminated” leaf activity remains the Current Activity, or an ancestor of the leaf activity becomes the Current Activity".
You're also correct that OP.1 ("Overall Sequencing Process") is mute on the topic, going so far as to say "Behavior not specified." for a Not Valid sequencing request. I believe the most common choice is the aforementioned "please select an activity from the activity tree" style screen in place of where the visible SCO would have been.
The spec tries very hard to separate LMS display choices from how sequencing itself operates. SN-5-3 states: "SCORM imposes no requirements on the type or style of the user interface presented to a learner at run-time, including any user interface devices for navigation. The nature of the user interface and the mechanisms for capturing interactions between the learner and the LMS are intentionally unspecified. Issues such as look and feel, presentation style and placement of user interface devices or controls are outside the scope of SCORM."
But the specification otherwise says some instructive things. Page SN-3-6 states that "As depicted in Figure 3.2.1c, the target of the Choice navigation request (Activity B) has a Sequencing Control Flow defined to be False. In this case, no activity can be identified for delivery (clusters cannot be delivered). Because Activity B has Sequencing Control Choice defined to be True, an LMS shall provide some mechanism for the learner to select (trigger a navigation request for) one of Activity B’s children directly, but not Activity B."
While this doesn't explicitly state that there should be instructive text displayed to the learner in that SCO area or that the choice shouldn't be allowed, it does state that something should be done that should guide the learner into intentionally performing another step to launch something else. Again, this isn't exactly the same use case, but it's probably the closest it gets relative to choice.

Seek to an offset via an external trigger

Currently I use the AcknoledgingMessageListener to implement a Kafka consumer using spring-Kafka. This implementation helps me listen on a specific topic and process messages with a manual ack.
I now need to build the following capability:
Let us assume that for an some environmental exception or some entry of bad data via this topic, I need to replay data on a topic from and to a specific offset. This would be a manual trigger (mostly via the execution of a Java class).
It would be ideal if I can retrieve the messages between those offsets and feed it is a replay topic so that a new consumer can process those messages thus keeping the offsets intact on the original topic.
CosumerSeekAware interface - if this is the answer how can I trigger this externally? Via let say a mvn -Dexec. I am not sure if this is even possible
Also let say that I have an crash time stamp with me, is it possible to introspect the topic to find the offset corresponding to the crash so that I can replay from that offset?
Can I find offsets corresponding to some specific data so that I can replay those specific offsets?
All of these requirements are towards building a resilience layer around our Kafka capabilities. I need all of these to be managed by a separate executable class that can be triggered manually providing the relevant data (like time stamps etc). This class should determine offsets and then seek to that offset, retrieve the messages corresponding to those offsets and post them to a separate topic. Can someone please point me in the right direction? I’m afraid I’m going around in circles.
so that a new consumer can process those messages thus keeping the offsets intact on the original topic.
Just create a new listener container with a different group id (new consumer) and use a ConsumerAwareRebalanceListener (or ConsumerSeekAware) to perform the seeks when the partitions are assigned.
Here is a sample CARL that seeks all assigned topics based on a timestamp.
You will need some mechanism to know when the new consumer should stop consuming (at which time you can stop() the new container). Maybe set max.poll.records=1 on the new consumer so he doesn't prefetch past the failure point.
I am not sure what you mean by #3.

More than one version of the flow in the Cordapp

I would like to have more than one version of certain flow pairs (both the InitiatingFlow and InitiatedBy) in a node's cordapps directory.
The reason for maintaining several copies of certain flow pairs is that some of the nodes may be using a previous version of the flow because they have yet to migrate the version of the flow.
As the flow's version is only in the annotation, I suspect there would be more than one class with the same fully-qualified name. This would result in a runtime error.
Can you provide an example of flow pairs with different versions that can remain in the same cordapps folder?
The correct approach here is not to define several flows pairs, but to use the flow version number in the InitiatingFlow to control how the corresponding InitiatedBy flow behaves.
For example, suppose we have an InitiatingFlow that:
Sends an Int in version 1
Sends a String in subsequent versions
The corresponding InitiatedBy flow may look like this:
#Suspendable
override fun call() {
val otherFlowVersion = otherSession.getCounterpartyFlowInfo().flowVersion
val receivedString = if (otherFlowVersion == 1) {
otherSession.receive<Int>().unwrap { it.toString() }
} else {
otherSession.receive<String>().unwrap { it }
}
}
By using the InitiatingFlow's version number, the InitiatedBy flow is able to communicate with parties running any version of the InitiatingFlow.
Note that there is no equivalent version number for the InitiatedBy flow, which means that the InitiatingFlow cannot condition its behaviour on the version of the InitiatedBy flow. The InitiatedBy flow is the side that must adapt to handle changes in the InitiatingFlow, and not vice-versa.
Additional information on flow versioning can be found here.
Corda has fairly limited versioning model for flows. For example, there is no way for the initiating flow to adapt its behaviour to work with an older version of the responding flow.
The way this could be worked around is through Protocol Handshake pattern. It works like the following. Implement a pair of subflows, say, InitiatorProtocolHandshakeFlow and RespondToProtocolHandshakeFlow. Make every initiating flow in the CorDapp that establishes a new flow session with another note to invoke InitiatorProtocolHandshakeFlow with the counterparty session, and make every responding flow to invoke RespondToProtocolHandshakeFlow with the counterparty session before sending or receiving anything else. Make the flows to negotiate relevant 'protocol features'. This could be implemented in a number of way, of which the simplest one to make the responder to send (and initiator to receive) a simple version number. Unlike flow version number in the annotation, the initiating flow can actually alter its behaviour based on the number provided by the responding flow.

how to invoke two different responder flows from the same parent flow?

I want to implement a use case where two responder flows (different classes) are initiated by the same parent flow.
I get the following exception:
java.lang.IllegalArgumentException: com.flow.initialFlows.InitialFlow has been specified as the initiating flow by both com.flow.responder.Responder1 and com.flow.responder2.Responder2
in How can I test two different responder flows in the same CorDapp? the suggestion was to use setCordappPackages(). this method is used in test scenarios and is part of the corda test package. what can you use outside test scenarios?
A single node cannot have two responder flows registered for the same initiating flow.
This is by design. Otherwise, the responding node know which of the two flows to invoke.

Correlation on MessageBox direct bound ports

I have an orchestration called MyUsefulOrch, hosted in an application MySharedApp.
MyUsefulOrch has an inbound messagebox-direct-bound port to receive requests, and after doing some useful work, an outbound messagebox-direct-bound port to send a message to the caller.
Now, I have another orchestration called MyCallerOrch which wants to benefit from the useful processing provided by MyUsefulOrch. However, MyCallerOrch is hosted in a different application, MyCallingApp.
I do not want to have any references to the assembly which contains MyUsefulOrch from MyCallerOrch.
My problem now is making sure I can send a message to MyUsefulOrch from MyCallerOrch and receive a response from it.
Ahah! Correlation should do the trick! But how do I go about getting correlation to work in this scenario?
For example:
Would I put a correlation id in a property schema and stuff a guid into the message context under this property from MyCallerOrch just before sending it to the messagebox?
How do I ensure that MyCallerOrch receives only the responses it needs to receive from MyUsefulOrch?
Do I need to put the correlation id value into the message body of the messages which are sent between the two orchestrations?
I would greatly appreciate any help, ideally as descriptive as possible, about how to acheive this.
Many thanks in advance.
If you use a two-way, request/response send port in the caller orchestration to send messages to the useful orchestration, then you can use correlation to route the relevant messages back to the userful orch from the caller.
The trick is that you will need to modify the useful orch (to make it more useful, of course).
If you do not/cannot control whether or not callers to the userful orch are expecting a response back, then you would need to make the inbound (request) port a one-way port. The orchestration would then complete by sending to a one-way outbound (response) port.
To ensure that messages received from two-way/request-response callers are routed back properly, the construct shape of the outbound message inside your useful orch will need to set the following message properties to true using a message assignment shape:
BTS.RouteDirectToTP
BTS.IsRequestResponse
Before setting those two properties, though, also make sure to do something like msgOut(*) f= msgIn(*); in the same message assignment shape to ensure that other properties get copied over. If the inbound and outbound messages are not the same, then you have to manually set each of the required properties, one at a time.
Those properties, of course, in addition to the two above, are what help ensure that the result of the useful orch is properly routed to the caller. They should be inside your correlation set and are:
BTS.CorrelationToken
BTS.EpmRRCorrelationToken
BTS.IsRequestResponse
BTS.ReqRespTransmitPipelineID
BTS.RouteDirectToTP
I'm getting a bit ahead of myself, however, as you assign the correlation set to the outbound send shape only if BTS.EpmRRCorrelationToken exists msgIn. This is critical. I have used a decision shape in an orchcestration, with the decision based upon that exact phrase. If the result is true, then send the previously constructed message out and assign the correlation set from above as the Initializing correlation set. This will cause BizTalk to route the message back to the caller as its expected response.
If the result of the decision was false then the caller of the useful orchestration was one-way. You will still likely want to send out a result (and just have someone else subscribe to it). You can even use the same send port as for two-way responses, just do not assign the correlation set.
You will want to thoroughly test this, of course. It does work for me in the one scenario in which I have used it, but that doesn't absolve others from doing their due diligence.
I think you are pretty much on the right track
Since the 2 applications are going to send messages to eachother, if you use strongly typed schemas, both apps will need to know about the schemas.
In this case recommend that you separate the common schemas off into a separate assembly, and reference this from both your orchestration apps.
(Schemas registered on the Server must have unique XMLNS#ROOTs, even across multiple applications)
However, if you really can't stand even a shared schema assembly reference, you might need to resort to untyped messages.
Richard Seroter has an example here
His article also explains a technique for auto stamping a correlation GUID on the context properties.
Edit : Good point. It is possible to promote custom context properties on the message without a Pipeline - see the tricks here and here - this would suffice to send the context property to MyUsefulOrch and similarly, the Custom context could be promoted on the return message from within MyUsefulOrch (since MyUsefulOrch doesn't need any correlation). However I can't think how, on the return to MyCallingOrch that the custom context property can be used to continue the "following correlation", unless you add a new correlating property into the return message.

Resources