Class ForkScanner
DepthFirstScanner
), but generally runs in linear order.
Warning This scanner has various issues when iterating over incomplete builds, or more generally
when multiple heads are passed to AbstractFlowScanner.setup(java.util.Collection<org.jenkinsci.plugins.workflow.graph.FlowNode>, java.util.Collection<org.jenkinsci.plugins.workflow.graph.FlowNode>)
, especially if the build contains nested parallelism.
Consider using DepthFirstScanner
instead in those cases to avoid these issues, employing its iteration order
as needed to sort results (although its global iteration order is inconsistent, its relative iteration order among
siblings at the same nesting level (e.g. parallel branches) is consistent and that is good enough for typical use
cases such as producing a tree).
In particular, the following known issues are possible when using this scanner with multiple heads:
- Nodes may be visited more than once (mainly with nested parallelism, but also possible with bad timing while a parallel step is initializing heads for its branches).
- Nodes that should be visited may be skipped (only known to happen for nested parallel steps with less than two branches).
- The ordering of branches in parallel steps is inconsistent with respect to the Pipeline script and the order may change as the build progresses.
- When using
visitSimpleChunks(java.util.Collection<org.jenkinsci.plugins.workflow.graph.FlowNode>, java.util.Collection<org.jenkinsci.plugins.workflow.graph.FlowNode>, org.jenkinsci.plugins.workflow.graphanalysis.SimpleChunkVisitor, org.jenkinsci.plugins.workflow.graphanalysis.ChunkFinder)
, some chunks may be skipped, and the boundaries of chunks may be incorrect. For example, in some cases, the start node inSimpleChunkVisitor.parallelStart(org.jenkinsci.plugins.workflow.graph.FlowNode, org.jenkinsci.plugins.workflow.graph.FlowNode, org.jenkinsci.plugins.workflow.graphanalysis.ForkScanner)
may not actually be the start node for a parallel step.
For completed builds, or when a single head is passed to AbstractFlowScanner.setup(java.util.Collection<org.jenkinsci.plugins.workflow.graph.FlowNode>, java.util.Collection<org.jenkinsci.plugins.workflow.graph.FlowNode>)
, the above issues should not occur and the following description should be accurate:
This is a fairly efficient way to visit all FlowNodes, and provides four useful guarantees:
- Every FlowNode is visited, and visited EXACTLY ONCE (not true for LinearScanner, which misses some)
- All parallel branches are visited before we move past the parallel block (not true for DepthFirstScanner)
- For parallels, we visit branches in reverse order (in fitting with end to start general flow)
- For EVERY block, the BlockEndNode is visited before the BlockStartNode (not true for DepthFirstScanner, with parallels)
The big advantages of this approach:
- Blocks are visited in the order they end (no backtracking) - helps with working a block at a time
- Points are visited in linear order within a block (easy to use for analysis)
- Minimal state information needed
- Branch information is available for use here
- Author:
- Sam Van Oort
-
Field Summary
Fields inherited from class org.jenkinsci.plugins.workflow.graphanalysis.AbstractFlowScanner
MAX_LIST_CHECK_SIZE, myBlackList, myCurrent, myNext
-
Constructor Summary
ConstructorDescriptionForkScanner
(Collection<FlowNode> heads) ForkScanner
(Collection<FlowNode> heads, Collection<FlowNode> blackList) -
Method Summary
Modifier and TypeMethodDescriptionReturn the node that begins the current parallel head, if we are known to be in a parallel blockorg.jenkinsci.plugins.workflow.graphanalysis.ForkScanner.NodeType
org.jenkinsci.plugins.workflow.graphanalysis.ForkScanner.NodeType
int
Return number of levels deep we are in parallel blocksstatic boolean
static boolean
boolean
If true, we are walking from the flow end node and have a complete view of the flow Needed because there are implications when not walking from a finished flow (blocks without aBlockEndNode
)next()
protected FlowNode
next
(FlowNode current, Collection<FlowNode> blackList) Actual meat of the iteration, get the next node to visit, using and updating state as neededprotected void
reset()
Reset internal state so that we can begin walking a new flow graph Public APIs need to invoke this before searchesprotected void
setHeads
(Collection<FlowNode> heads) Set up to begin flow scanning using the filteredHeads as starting points This method makes several assumptions: -AbstractFlowScanner.reset()
has already been invoked to reset state - filteredHeads has already had any points inAbstractFlowScanner.myBlackList
removed - none of the filteredHeads are nullstatic void
setParallelStartPredicate
(com.google.common.base.Predicate<FlowNode> pred) Deprecated.static void
visitSimpleChunks
(Collection<FlowNode> heads, Collection<FlowNode> blacklist, SimpleChunkVisitor visitor, ChunkFinder finder) static void
visitSimpleChunks
(Collection<FlowNode> heads, SimpleChunkVisitor visitor, ChunkFinder finder) void
visitSimpleChunks
(SimpleChunkVisitor visitor, ChunkFinder finder) Walk through flowsMethods inherited from class org.jenkinsci.plugins.workflow.graphanalysis.AbstractFlowScanner
allNodes, allNodes, convertToFastCheckable, filter, filteredNodes, filteredNodes, filteredNodes, filteredNodes, findFirstMatch, findFirstMatch, findFirstMatch, findFirstMatch, hasNext, iterator, remove, setup, setup, setup, setup, visitAll, visitAll
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
Methods inherited from interface java.lang.Iterable
forEach, spliterator
Methods inherited from interface java.util.Iterator
forEachRemaining
-
Constructor Details
-
ForkScanner
public ForkScanner() -
ForkScanner
-
ForkScanner
-
-
Method Details
-
getCurrentType
@CheckForNull public org.jenkinsci.plugins.workflow.graphanalysis.ForkScanner.NodeType getCurrentType() -
getNextType
@CheckForNull public org.jenkinsci.plugins.workflow.graphanalysis.ForkScanner.NodeType getNextType() -
reset
protected void reset()Description copied from class:AbstractFlowScanner
Reset internal state so that we can begin walking a new flow graph Public APIs need to invoke this before searches- Specified by:
reset
in classAbstractFlowScanner
-
setParallelStartPredicate
@Deprecated public static void setParallelStartPredicate(@NonNull com.google.common.base.Predicate<FlowNode> pred) Deprecated.Now a complete no-op -- originally this was a workaround for dependency issues with workflow-cps. Specifically, requiring classes from workflow-cps to detect if something is a parallel step. -
isParallelStart
-
isParallelEnd
-
isWalkingFromFinish
public boolean isWalkingFromFinish()If true, we are walking from the flow end node and have a complete view of the flow Needed because there are implications when not walking from a finished flow (blocks without aBlockEndNode
) -
setHeads
Description copied from class:AbstractFlowScanner
Set up to begin flow scanning using the filteredHeads as starting points This method makes several assumptions: -AbstractFlowScanner.reset()
has already been invoked to reset state - filteredHeads has already had any points inAbstractFlowScanner.myBlackList
removed - none of the filteredHeads are null- Specified by:
setHeads
in classAbstractFlowScanner
- Parameters:
heads
- Head nodes that have been filtered against denyList
-
getCurrentParallelStartNode
Return the node that begins the current parallel head, if we are known to be in a parallel block- Returns:
- The FlowNode that marks current parallel start
-
getParallelDepth
public int getParallelDepth()Return number of levels deep we are in parallel blocks -
next
- Specified by:
next
in interfaceIterator<FlowNode>
- Overrides:
next
in classAbstractFlowScanner
-
next
Description copied from class:AbstractFlowScanner
Actual meat of the iteration, get the next node to visit, using and updating state as needed- Specified by:
next
in classAbstractFlowScanner
- Parameters:
current
- Current node to use in generating next valueblackList
- Nodes that are not eligible for visiting- Returns:
- Next node to visit, or null if we've exhausted the node list
-
visitSimpleChunks
public static void visitSimpleChunks(@NonNull Collection<FlowNode> heads, @NonNull Collection<FlowNode> blacklist, @NonNull SimpleChunkVisitor visitor, @NonNull ChunkFinder finder) -
visitSimpleChunks
public static void visitSimpleChunks(@NonNull Collection<FlowNode> heads, @NonNull SimpleChunkVisitor visitor, @NonNull ChunkFinder finder) -
visitSimpleChunks
Walk through flows
-