Nav

Foreach Scope

The Foreach scope splits a collection into elements and processes them iteratively through the processors embedded in the scope, then returns the original message to the flow.

Foreach vs. Splitter and Aggregator Pairs

Typically, to create a flow that processes messages containing a collection of elements you must use one flow control to split the collection into individual elements, which the flow processes iteratively, then you must use another flow control to re-aggregate the elements into a new collection so they can be passed out of the flow. Several of Mule’s flow controls support such splitting-and-re-aggregation, but they cannot match the convenience of the Foreach scope.

This use of splitter and aggregator pairs also presents several potential limitations:

  • Under some circumstances, splitting a message collection into pieces can cause certain vital bits of XML — metadata in the header or footer, for example — to be dropped from the re-aggregated XML.

  • When you split certain collection types — Java, for example — into many pieces for processing, the collection may be re-aggregated into a different collection type — MuleMessageCollection, for example. (As a result, you may need to add extra flow steps to transform the processed message collection back into its original collection type.)

  • When you split, process, or aggregate a message collection, you must choose among several splitter and aggregator types. Sometimes, it proves difficult to determine which splitter/aggregator combination best suits your message processing needs.

Foreach provides several advantages over splitter-aggregator pairs:

  • Foreach splits collections into elements, then processes them iteratively without losing any of the message payload.

  • After Foreach splits a message collection and processes the individual elements, it doesn’t re-aggregate those individual elements into a MuleMessageCollection; rather, it returns the original message. (This results in "Java in, Java out" rather than "Java in, MuleMessageCollection out.")

  • The Foreach scope is versatile; it can iteratively process elements from any type of collection, including maps, lists, arrays, and MuleMessageCollections.

  • The Foreach scope can split and process collections of elements that are not part of the message payload. For example, Foreach can process message property collections (metadata) from the message header.

If the elements are mutable objects (for example, a bean with different fields in it) and any of the message processors embedded in your Foreach scope modifies a value of the original object (for example, one of the fields), then the changes to the fields persist once the foreach returns the original message to the flow.

In other words, Foreach does not make a deep copy of the data when processing a message nor when returning the original message to the flow.

Configuring Foreach

  1. Add a Foreach scope in your flow at the point where you want to initiate the Foreach scope processing.

    for+each

  2. Insert one or more message processors inside the Foreach scope area to define how Mule should process each element within the message collection. The Foreach scope can contain any number of message processors as well as references to child flows.

    The only type of message processor you cannot drag into a Foreach scope is an inbound connector.

    If you drag a two-way connector into a Foreach scope, Mule automatically converts it to an outbound-only connector.

    foreach

    Field Description

    Display Name

    Default value = For Each - Customize to display a unique name for the scope in your application.

    XML Example:

    doc:name="For Each"

    Collection

    (Optional) No default value. - Enter an expression that tells Foreach where to find the data it must split and process. For example, enter an expression that instructs Foreach to split and process a collection from the header section – rather than the payload. Unless this field specifies otherwise, Foreach assumes that the message payload is the collection.

    XML Example:

    collection="#[payload.topic]"

    Counter Variable Name

    (Optional) Default value = counter - Enter a name in this field to label the variable that Foreach uses to record the number of the elements it has processed. If your collection already uses the label counter for another variable, this field is blank and you need to enter a different label for the Counter Variable Name, such as index.

    XML Example:

    counterVariableName="counter"

    Batch Size

    (Optional) Default value = 1 - Enter an integer to indicate the number of elements in each batch that Foreach processes. Potentially, these batches promote quicker processing. If greater than one, each batch is treated as a separate Mule message. For example, if a collection has 200 elements and you set the batch size to 50, Foreach iteratively processes 4 batches of 50 elements, each as a separate Mule message.

    XML Example:

    batchSize="50"

    Root Message Variable Name

    (Optional) Default value = rootMessage - Enter a name in this field to label the variable that Foreach uses to reference the complete, unsplit message collection. If your collection already uses the label rootMessage for another variable, this field is blank and you need to enter a different label for the Root Message Variable Name.

    XML Example:

    rootMessageVariableName="rootMessage"
  1. Add a foreach element to your flow at the point where you want to initiate a foreach processing block. Refer to the code sample below.

    Element Description

    foreach

    Use to create a block of message processors that iteratively process elements within a collection.

  2. Configure the scope according to the table below.

    Element Attribute Default Value Description

    doc:name

    For Each

    Customize to display a unique name for the async scope in your application.

    Note: Attribute not required in Mule Standalone configuration.

    collection

    Payload

    (Optional) Enter an expression that tells Foreach where to find the data it must split and process. For example, enter an expression that instructs Foreach to split and process a collection from the header section – rather than the payload. Unless this attribute specifies otherwise, Foreach assumes that the message payload is the collection.

    counterVariableName

    counter

    (Optional) Specify to label the variable that Foreach uses to record the number of the elements it has processed. If your collection already uses the label counter for another variable, select a unique name.

    batchSize

    1

    (Optional) Specify an integer to indicate the number of elements in each batch that Foreach processes. Potentially, these batches promote quicker processing. For example, if a collection has 200 elements and you set the batch size to 50, Foreach iteratively processes 4 batches of 50 elements.

    rootMessageVariableName

    rootMessage

    (Optional) Specify to label the variable that Foreach uses to reference the complete, unsplit message collection. If your collection already uses the label rootMessage for another variable, select a unique name.

  3. Add nested elements beneath your foreach element to define how Mule should process each element within the message collection. The Foreach scope can contain any number of message processors as well as references to child flows.

    
           
                   
                
    1
    2
    3
    4
    
    <foreach collection="#[payload.name]" doc:name="For Each" counterVariableName="counter" rootMessageVariableName="rootMessage" batchSize="5">
        <some-nested-element/>
        <some-other-nested-element/>
    </foreach>

Foreach Error Handling

The exception strategy defined for your flow handles all the exceptions thrown within the Foreach scope. (If you have not explicitly defined an exception strategy for your flow, Mule implicitly applies the default exception strategy to handle exceptions.) If a message in a collection throws an exception, Foreach stops processing that collection and invokes the exception strategy.

For example, Foreach throws an IllegalArgumentException whenever two conditions hold true:

  • It receives a message payload that is not a collection

  • You have not identified a message collection outside the message payload (defined by entering an expression in the Collection field in the Studio Visual Editor or including the collection attribute in XML configuration.)

Considerations when Persisting Data

In case the message inside the foreach scope is persisted, not only the item in the collection is serialized but also all the variables associated with the current message. The rootMessage variable, associated with the message, contains a reference of the complete, unsplit message collection that could potentially be holding thousands of items. Therefore, serialization/deserialization of the rootMessage variable could impact memory consumption considerably when this collection is large enough.

To avoid this issue you must first remove the rootMessage variable from the message before persisting it. For this you can use the <remove-variable> element like so:

<remove-variable variableName="rootMessage" doc:name="Variable"/>

In Studio, you can drag a Variable message processor inside your scope and set it to "Remove Variable".

Example

The following example illustrates a flow that uses Foreach to add information to each message in a collection.

The HTTP connector receives a request from a client, then queries a JDBC database, where a table indicates the model names and the model years of various cars. Foreach breaks the collection (the table) apart into a list of elements (rows), each of which contains information such as about individual elements (maps) model:'ford sierra', model_year=1982}}. Foreach sends each element through the message processors in its scope.

The flow adds a new entry to each element’s map; if the model year is less than 2001, Mule adds type='20th century car', then sends the element to the JMS connector; otherwise, Mule adds type='21st century car' and sends the element to the File connector. Foreach returns a collection at the end of the flow and sends it to the transformer.

This particular example replaces the main flow’s default exception strategy with a custom Catch Exception Strategy that leverages the Set Payload and HTTP Response Builder building blocks.

for+each+example

Complete Example Code


         
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns:db="http://www.mulesoft.org/schema/mule/db" xmlns:spring="http://www.springframework.org/schema/beans" xmlns:jdbc-ee="http://www.mulesoft.org/schema/mule/ee/jdbc" xmlns="http://www.mulesoft.org/schema/mule/core"
      xmlns:http="http://www.mulesoft.org/schema/mule/http"
      xmlns:file="http://www.mulesoft.org/schema/mule/file"
      xmlns:jdbc="http://www.mulesoft.org/schema/mule/jdbc"
      xmlns:jms="http://www.mulesoft.org/schema/mule/jms"
      xmlns:scripting="http://www.mulesoft.org/schema/mule/scripting"
      xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
      xmlns:core="http://www.mulesoft.org/schema/mule/core"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/file http://www.mulesoft.org/schema/mule/file/current/mule-file.xsd
http://www.mulesoft.org/schema/mule/ee/jdbc http://www.mulesoft.org/schema/mule/ee/jdbc/current/mule-jdbc-ee.xsd
http://www.mulesoft.org/schema/mule/jms http://www.mulesoft.org/schema/mule/jms/current/mule-jms.xsd
http://www.mulesoft.org/schema/mule/scripting http://www.mulesoft.org/schema/mule/scripting/current/mule-scripting.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/db http://www.mulesoft.org/schema/mule/db/current/mule-db.xsd">

    <jms:activemq-connector name="JMSConnector" doc:name="Active MQ"></jms:activemq-connector>
    <http:listener-config name="HTTP_Listener_Configuration" host="localhost" port="9091" doc:name="HTTP Listener Configuration"/>
    <db:derby-config name="Derby_Configuration" url="jdbc:derby:${app.home}/muleEmbeddedDB;create=true"   doc:name="Derby Configuration"/>

    <flow name="process" >
        <http:listener config-ref="HTTP_Listener_Configuration" path="process" doc:name="HTTP">
            <http:error-response-builder statusCode="500" reasonPhrase="You need to populate the Database first"/>
        </http:listener>
        <db:select config-ref="Derby_Configuration" doc:name="Database">
            <db:parameterized-query><![CDATA[SELECT * FROM cars]]></db:parameterized-query>
        </db:select>
        <foreach doc:name="Foreach">
            <choice doc:name="Choice">
                <when expression="payload.'MODEL_YEAR' &#38;lt; 2001">
                    <processor-chain doc:name="Processor Chain">
                        <expression-component doc:name="Set payload type"><![CDATA[payload.'TYPE' = '20th century car']]></expression-component>
                        <jms:outbound-endpoint connector-ref="JMSConnector" queue="in" doc:name="JMS"></jms:outbound-endpoint>
                    </processor-chain>
                </when>
                <otherwise>
                    <processor-chain doc:name="Processor Chain">
                        <expression-component doc:name="Set payload type">payload.'TYPE'='21st century car'</expression-component>
                        <file:outbound-endpoint path="/tmp" responseTimeout="10000" doc:name="File"></file:outbound-endpoint>
                    </processor-chain>
                </otherwise>
            </choice>
        </foreach>
        <set-payload value="#[payload.size()] cars where processed: #[payload]" doc:name="Set response"></set-payload>
        <parse-template location="foreach_info.html" doc:name="Parse Template"/>
        <catch-exception-strategy doc:name="Catch Exception Strategy">
            <parse-template location="foreach_error.html" doc:name="Parse Template"/>
        </catch-exception-strategy>
    </flow>
    <flow name="populate" >
         <http:listener config-ref="HTTP_Listener_Configuration" path="populate" doc:name="HTTP">
            <http:error-response-builder statusCode="500" reasonPhrase="DB already populated"/>
        </http:listener>

        <scripting:component doc:name="Script to populate DB">
            <scripting:script engine="Groovy">
                <scripting:text><![CDATA[jdbcConnector = muleContext.getRegistry().lookupConnector("JDBCConnector");
qr = jdbcConnector.getQueryRunner();
conn = jdbcConnector.getConnection();
qr.update(conn, "CREATE TABLE cars (model varchar(256), model_year integer)");
qr.update(conn, "INSERT INTO cars values('Ford Sierra', 1982)");
qr.update(conn, "INSERT INTO cars values('Opel Astra', 2001)");]]></scripting:text> </scripting:script>
        </scripting:component> <set-payload value="Successfully populated the database" doc:name="Set Payload"></set-payload>
        <parse-template location="foreach_info.html" doc:name="Parse Template"/>
        <catch-exception-strategy doc:name="Catch Exception Strategy">
            <parse-template location="foreach_error.html" doc:name="Parse Template"/>
        </catch-exception-strategy> </flow>
</mule>