package ee.telekom.workflow.graph.node.gateway;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import ee.telekom.workflow.graph.Graph;
import ee.telekom.workflow.graph.GraphEngine;
import ee.telekom.workflow.graph.GraphInstance;
import ee.telekom.workflow.graph.Node;
import ee.telekom.workflow.graph.Token;
import ee.telekom.workflow.graph.Transition;
import ee.telekom.workflow.graph.node.gateway.condition.Condition;
/**
* Gateway, that implements the "multiple choice" workflow pattern. That means, based on the incoming
* token, it generates a new child token per subsequent branch with a runtime condition that evaluates
* to true and initiates the execution of the child tokens. At the same time, the incoming token remains at this gateway.
* <p>
* The underlying token model implies restraints on the subsequent branches. The following figure illustrates
* the two types of valid usages.<br>
* One valid pattern is the balanced fork-join block which is built of a fork and a join. It synchronises (re-joins)
* all branches leaving the fork in the paired join. As a join, either an {@link AndJoin} or a
* {@link CancellingDiscriminator} may be used but not an {@link XorJoin}.<br>
* The other valid pattern leaves all subsequent branches independently and never synchronises (re-joins)
* those back. In that case, every subsequent branch is ended implicitly by a node without outgoing
* transitions.
* <pre>
* +-(condition1)-[3]--+
* [1]--[OR] [AND/CD]--[6]
* +-(condition2)-[4]--+
*
* +-(condition1)-[3]
* [1]--[OR]
* +-(condition2)-[4]
* </pre>
* Note that while an {@link AndFork} executes all subsequent branches, this kind of gateway selects 0 to all
* subsequent branches depending on the transition conditions. If no subsequent branches is selected, the incoming
* token is terminated.
*/
public class OrFork extends AbstractConditionalGateway{
public OrFork( int id ){
super( id );
}
public OrFork( int id, String name ){
super( id, name );
}
@Override
public void execute( GraphEngine engine, Token token ){
GraphInstance instance = token.getInstance();
Graph graph = instance.getGraph();
Node node = token.getNode();
List<Pair<Token, String>> pairs = new LinkedList<Pair<Token, String>>();
for( Pair<Condition, String> pair : getConditions() ){
Condition condition = pair.getLeft();
String transitionName = pair.getRight();
if( condition == null || condition.evaluate( instance ) ){
Transition transition = graph.getOutputTransitions( node, transitionName );
if( transition != null ){
Token child = engine.addToken( instance, node, token );
pairs.add( Pair.of( child, transitionName ) );
}
}
}
for( Pair<Token, String> pair : pairs ){
if( pair.getLeft().isActive() ){
engine.complete( pair.getLeft(), null, pair.getRight() );
}
}
if( pairs.isEmpty() ){
// No suitable branch was found. Terminating the token's execution
engine.terminate( token );
}
}
// This given token can either be a "parent" token or a leave token.
//
// The parent token remains at this node until the created leave tokens
// are synchronised (either by an AndJoin or XorJoin node).
//
// Leave tokens remain at this node until their execution begins. Note,
// that is is possible that a leave token is cancelled before its execution
// begins. This happens if an earlier executed thread reaches a cancelling
// discriminator.
@Override
public void cancel( GraphEngine engine, Token token ){
GraphInstance instance = token.getInstance();
Collection<Token> childTokens = instance.getActiveChildTokens( token );
for( Token childToken : childTokens ){
engine.cancel( childToken );
}
}
}