Carry out the following steps:
<assign>
activity after the <invoke>
activity named invoke_search_web_search
, double-click on it, and go to the Copy Rules tab. Create a new copy rule and then choose $invoke_search_web_service_search_OutputVariable.output
as the From expression and $outputVariable.payload
as the To expression. Then right-click on that copy rule and go to Change rule type and select Append. Then click on Apply and then on OK, as shown in the following screenshot:In the preceding section, we collated all the responses from repetitive web service invocations into a single variable called outputVariable
.
Frequently in real life, we need to perform some tasks repeatedly. This typical behavior is common for computer programs, as well. Almost all the programming languages define language constructs that support to execute repetitive tasks. Similarly, WS-BPEL 2.0 also provides <while>
, <repeatUntil>
, and <forEach>
activities to support repetitive execution in different ways. In contrast, the <forEach>
activity supports repetitive execution of the contained activity in a parallel manner for a predefined number of times. Let's discuss the <forEach>
activity in detail in the coming sections.
As we introduced the <forEach>
activity in the previous section, in order to determine the number of repetitions, there are two elements named <startCounterValue>
and <finalCounterValue>
, as shown:
<forEach counterName="BPELVariableName" parallel="yes|no"> <startCounterValue> unsigned-integer-expression </startCounterValue> <finalCounterValue> unsigned-integer-expression </finalCounterValue> <scope> ... </scope> </forEach>
The contained activity is executed N + 1 times, where N is equal to value of <finalCounterValue>
minus the value of <startCounterValue>
.
When the <forEach>
activity is started, the expression values of <startCounterValue>
and <finalCounterValue>
are evaluated and those values remain constant during the lifespan of the activity.
One important thing to notice is that the <forEach>
activity can only contain the <scope>
activity as the top-level structured activity. All the activities that are supposed to be executed within the <forEach>
activity should be included within the top-level <scope>
activity.
Additionally, the <forEach>
activity has an attribute named counterName
. The attribute refers to a variable that is implicitly declared in the contained scope of the <forEach>
activity. The variable has the value of current counter during the repetitive execution. In other words, the contained <scope>
can refer to the current counter value during each execution by accessing the variable referred by counterName
. In other words, at each repetition, the contained <scope>
sees a different value for this variable. During the first iteration, the contained <scope>
activity sees the value of the variable referred by counterName
, as the value of the <startCounterValue>
element. Similarly, during the final iteration, the contained activity sees the value of the variable referred by counterName
, as the value of <finalCounterValue>
element.
The remaining attribute in the <forEach>
activity is the attribute named parallel
. This attribute enables to execute the contained <scope>
activity in parallel or serial. This is very important and one significant difference between other activities such as <while>
and <repeatUntil>
that support repetitive executions. If the attribute value is yes
, then all the repetitive executions start in parallel. Otherwise, the loop branches are executed one after the other. The <forEach>
activity has one important advantage of simple definition of parallel executions. We discuss more about this parallel <forEach>
loop in the next section.
We discussed the attributes of the <forEach>
activity and the top-level child activities such as <startCounterValue>
and <finalCounterValue>
. These parameters are required enough to define a simple repetitive behavior that is executed serially or in parallel. There is one optional top-level child element left that can be used to define completion condition for the <forEach>
activity, which is the <completionCondition>
element. This element is defined as follows:
<forEach counterName="BPELVariableName" parallel="yes|no"> <startCounterValue> unsigned-integer-expression </startCounterValue> <finalCounterValue> unsigned-integer-expression </finalCounterValue> <completionCondition> <branches successfulBranchesOnly="yes|no"> unsigned-integer-expression </branches> </completionCondition> <scope> ... </scope> </forEach>
The <completionCondition>
element enables us to terminate the repetitive execution even before completing all the loop branches. The <forEach>
activity completes when all the loop branches are completed unless there is a <completionCondition>
element. If the <completionCondition>
exists, we can specify that only specific number of loop branches get completed in order to complete the <forEach>
activity. We can configure this by the value of the <branches>
element that is defined within the <completionCondition>
element. The value is an unsigned integer, which can be defined using an XPath expression.
However, what if we only need to complete the <forEach>
activity when only a specific number of successful loop branches are completed rather counting both successful and failed loop branches. This can be configured using the attribute named successfulBranchesOnly
within the <branches>
element. We discussed the configuration parameters of the <completionCondition>
element. Now, we discuss how it affects the behavior of the <forEach>
activity. At the end of each iteration, the <forEach>
activity evaluates the <completionCondition>
element and if the completion condition is met, the <forEach>
activity does not trigger further iterations.
Let's explore all the discussed parameters with a simple example.
Suppose you needed to collect ten random images from a set of hundred image sources. However, out of those hundred image sources, some of them can be unreliable at some times. The BPEL process should invoke all those image sources and retrieve ten images from images sources that are reliable at that time. This problem can be easily implemented using the <forEach>
activity with a completion condition as follows:
<forEachcounterName="BPELVariableName" parallel="no"> <startCounterValue> fn:number('1') </startCounterValue> <finalCounterValue> fn:number('100') </finalCounterValue> <completionCondition> <branches successfulBranchesOnly="yes"> fn:number('10') </branches> </completionCondition> <scope> <invoke... /><!-- Invoking the image source and retrieve the image--> <sequence... /><!-- Necessary steps to persist the retrieved image --> </scope> </forEach>
Counter for the <forEach>
activity starts from 1
and it increments until 100
. During the start of each execution, the BPEL runtime evaluates the completion condition. In this example, the BPEL runtime checks whether there is at least ten successful branches completed at the time of evaluation. If so, the execution of the <forEach>
activity is marked as completed. In the example, the loop branches are executed in a serial order. In the next section, we discuss the behavior of the <forEach>
activity when the loop branches are executed in parallel.
In the preceding section, we discussed all the parameters that configure the behavior of the <forEach>
activity. One parameter that can make a significant impact on the behavior of the <forEach>
activity is the attribute named parallel
. This is because if the attribute's value is set to be true
, the loop branches are executed in parallel. Also, the resultant behavior, when the completion condition meets, also changes.
Also, in some cases, a BPEL developer has to implement the activities within the enclosed scope differently based on the execution manner (that is, serial or parallel). One example would be, if the enclosed scope reads and modifies a global variable, then the scopes should be implemented as isolated scopes (we discussed about isolated scopes in the preceding chapter) to avoid unexpected behavior. In a serial execution, the scope can be either isolated or not. However, we will not go into details on this in this book.
In the upcoming subsections, we discuss the behavior of the parallel <forEach>
activity.
At the start of a parallel <forEach>
activity, the expression values of <startCounterValue>
and <finalCounterValue>
are evaluated and based on those values, the required number of iterations are determined. Then based on those required number of iterations, the enclosed <scope>
is executed concurrently. Even though, the behavior of the variable specified by counterName
is similar to the serial <forEach>
execution. Each enclosed <scope>
execution sees a variable specified by the counterName
attribute with a unique integer value. This unique integer value spans from the expression value of <startCounterValue>
to the expression value of <finalCounterValue>
.
The completion condition is evaluated at the end of each iteration. The interesting fact in a parallel <forEach>
activity is, at the end of an iteration, we do not know whether the other concurrent enclosed scopes are completed or not. However, in a serial <forEach>
activity, we know that there is no instance of enclosed <scope>
activities at the end of an iteration. So unlike in a serial <forEach>
, if the completion condition is met, then all the running enclosed <scope>
activities should be terminated. Consequently, the termination handler of each enclosed <scope>
is triggered.
In the previous section, we discussed the behavior of the parallel <forEach>
activity. Also, we already discussed about the <flow>
activity in Chapter 2, Service Invocation. Both these constructs support parallel execution of its enclosed activities. The BPEL developer have the option of using the <flow>
activity or <forEach>
activity to implement the parallel execution. Let's discuss what is lacking in the <forEach>
activity compared to the <flow>
activity in terms of parallel execution.
When there are concurrent executions happening, sometimes the BPEL developer needs to define some coordination among those executions. The <flow>
activity utilizes constructs (for example, <links>
, <sources>
, <targets>
, and so on), which support to define synchronization dependencies. However, the parallel <forEach>
doesn't have such support. Rather, it executes the enclosed <scope>
activity concurrently without any concern of synchronization among such iterations. The <flow>
activity should be used when there is a requirement of a coordinated execution.
The parallel <forEach>
activity is very useful when implementing inherently parallel set of similar tasks. Those tasks have no dependency among themselves. Also, the tasks should be similar, for example, invoking a web service. In such a scenario, using the <flow>
activity just only incurs an excess amount work.
So based on those reasons, the BPEL developer should choose the better option among the parallel <forEach>
activity and <flow>
activity.
Q1. What are the values that do not change over the time of an execution of a <forEach>
loop?
counterName
attribute)<startCounterValue>
)finalCounterValue>
)<branches>
)Q2. What will happen if the <startCounterValue>
is larger than the <finalCounterValue>
?
Q3. What will happen if the value of <branches>
exceeds the possible number of iterations?
Q4. What can be the child activity of a <forEach>
activity?
<sequence>
<flow>
<scope>
<invoke>