WSO2 BPS - Extensions - Iterable ForEach

Iterable ForEach


Introduction

This extension simplifies a common usage pattern in which forEach is used to iterate over a selected sequence of nodes. The common use case involves selecting a sequence of nodes, storing it in a scope variable, and using forEach to iterate over that sequence, using the current counter value to extract and operate on the indexed value from the sequence. This extension captures the pattern in a form that's easier to author and debug, by replacing counter with iterator and eliminating the use of temporary variables.


Processing Multiple Branches - ForEach

The <forEach> activity will execute its contained <scope> activity exactly M times where M is the number of items in the <sequenceValue>.

<forEachrangeName="BPELVariableName"parallel="yes|no"   standard-attributes>
   standard-elements
   <sequenceValueexpressionLanguage="anyURI"? instanceOf="sequenceType">
      unsigned-integer-expression
   </sequenceValue><completionCondition>?
      <branchesexpressionLanguage="anyURI"?successfulBranchesOnly="yes|no"?>?
         unsigned-integer-expression
      </branches></completionCondition><scope ...>...</scope></forEach>

When the <forEach> activity is started, the expression in <sequenceValue> is evaluated. Once that value is returned it remains constant for the lifespan of the activity. That expressions MUST return a sequence of items (meaning it comprises nodes, texts or atomic values), where each item can be validated to be the type specified by the instanceOf attribute. If that expression does not return a valid sequence value, a bpel:invalidExpressionValue fault will be thrown (see section 8.3. Expressions). If the <sequenceValue> is empty, then the child <scope> activity MUST NOT be performed and the <forEach> activity is complete.

The child activity of a <forEach> MUST be a <scope> activity. The <forEach> construct introduces an implicit range variable, and also introduces dynamic parallelism (i.e. having parallel branches of which number is not known ahead of time). The <scope> activity provides a well-defined scope snapshot semantic and a way to name the dynamic parallel work for compensation purposes (see scope snapshot description in section 12.4.2. Process State Usage in Compensation Handlers).

If the value of the parallel attribute is no then the activity is a serial <forEach>. The enclosed <scope> activity MUST be executed M times, each instance starting only after the previous repetition is complete. If premature termination occurs such as due to a fault, or the completion condition evaluates to true, then this M requirement does not apply. During each repetition, a variable of type specified by the instanceOf attribute is implicitly declared in the <forEach> activity's child <scope>. This implicit variable has the name specified in the rangeVariableName attribute. The first iteration of the scope will see the range variable initialized to the first item in <sequenceValue>. The next iteration will cause the range variable to be initialized to the second item in <sequenceValue>. Each subsequent iteration will move the range variable to the next item in the sequence until the final iteration where the range will be set to the last item in <sequenceValue>. The range variable is local to the enclosed <scope> and although its value can be changed during an iteration, that value will be lost at the end of each iteration. Therefore, the range variable value will not affect the value of the next iteration's range.

If the value of the parallel attribute is yes then the activity is a parallel <forEach>. The enclosed <scope> activity MUST be concurrently executed M times. In essence an implicit <flow> is dynamically created with M copies of the <forEach>'s enclosed <scope> activity as children. Each copy of the <scope> activity will have the same range variable declared in the same manner as specified for serial <forEach>. Each instance's range variable MUST be uniquely initialized in parallel to one of the item values starting with first and up to and including the last item in <sequenceValue>, as a part of <scope> instantiation.

If a variable of the same name as the value of the rangeName attribute is declared explicitly in the enclosed scope, it would be considered a case of duplicate variable declaration and MUST be reported as an error during static analysis.

The <forEach> activity without a <completionCondition> completes when all of its child <scope>'s have completed. The <completionCondition> element is optionally specified to prevent some of the children from executing (in the serial case), or to force early termination of some of the children (in the parallel case).

The <branches> element represents an unsigned-integer expression (see section 8.3.4. Unsigned Integer Expressions) used to define a completion condition of the "at least N out of M" form. The actual value B of the expression is calculated once, at the beginning of the <forEach> activity. It will not change as the result of the <forEach> activity's execution. At the end of execution of each directly enclosed <scope> activity, the number of completed children is compared to B, the value of the <branches> expression. If at least B children have completed, the <completionCondition> is triggered: No further children will be started, and currently running children will be terminated (see section 12.6 Termination Handlers). Note that enforcing the semantic of "exactly N out of M" in parallel <forEach> would involve a race condition, and is therefore not specified.

When the completion condition B is calculated, if its value is larger than the number of directly enclosed activities M, then the standard bpel:invalidBranchCondition fault MUST be thrown. Both B and M may be constant expressions, and in such cases, static analysis SHOULD reject processes where it can be detected that B is greater than M.

The <branches> element has an optional successfulBranchesOnly attribute with the default value of no. If the value of successfulBranchesOnly is no, all <scope>'s which have completed (successfully or unsuccessfully) MUST be counted. If successfulBranchesOnly is yes, only <scope>'s which have completed successfully MUST be counted.

The <completionCondition> is evaluated each time a directly enclosed <scope> activity completes. If the <completionCondition> evaluates to true, the <forEach> activity completes:

If upon completion of a directly enclosed <scope> activity, it can be determined that the <completionCondition> can never be true, the standard bpel:completionConditionFailure fault MUST be thrown.

When a <completionCondition> does not have any sub-elements or attributes understood by the WS-BPEL processor, it MUST be treated as if the <completionCondition> does not exist.