I have an array list values which I loop using foreach and choice. As soon as the first success happens in choice I want to come out of the foreach loop. And I don't want continue to check remaining conditions. In mulesoft flow
Thank you in advance.
You might not be able "break" foreach loop, but you can always achieve this functionality using expression/groovy script. Please refer this: Is there a break statement in Mule <foreach>
Please use Break Statement for exit from foreach loop according to your condition true.
As you have mentioned in your use case
Wrap the message filter inside your for-loop.
<foreach doc:name="For Each" collection="#[]">
<message-filter onUnaccepted="flow" >
<expression-filter expression="#[]"/>
</message-filter>
</foreach>
First of all you should find out on which index you will get success message by using indexOf method. Then after use subList method to get the required list and use that list in Foreach loop.
<foreach collection="#[payload.subList(0,3)]" doc:name="For Each">
<logger message="#[payload]" level="INFO" doc:name="Logger"/>
</foreach>
Use Groovy
You can control the flow of a Groovy script much easier than a traditional "for-each" transformer. In your particular case, your groovy script would look something (but not exactly) like:
<scripting:component doc:name="Groovy">
<scripting:script engine="Groovy"><![CDATA[// Define the collection to loop
def loopCollection = flowVars.nameOfCollectiontoLoop
// Define the condition which needs to be met
def exitCondition = "Some condition to match"
// Loop the collection to do what you need
loopCollection.each { row ->
if (row.identifierName == exitCondition)
{
return
}
else
{
// Continue your processing
}
}
return payload]]></scripting:script>
</scripting:component>
Use a Choice strategy to stop processing
The other way which I can think of is to use a Choice as soon as you enter the loop to see if the condition has been previously met, if so, do nothing; as follows:
<flow name="testsFlow">
<set-variable variableName="conditionMet" value="#[false]" doc:name="conditionMet"/>
<foreach doc:name="For Each">
<choice doc:name="Choice">
<when expression="#[(flowVars.conditionMet == true)]">
<scripting:component doc:name="Do nothing">
<scripting:script engine="Groovy"/>
</scripting:component>
</when>
<otherwise>
<scripting:component doc:name="Continue Loop">
<scripting:script engine="Groovy"><![CDATA[// Define the collection to loop
def loopCollection = flowVars.nameOfCollectiontoLoop
// Define the condition which needs to be met
def exitCondition = "Some condition to match"
// Define the "conditionMet" variable
def conditionMet = flowVars.conditionMet
// Loop the collection to do what you need
loopCollection.each { row ->
if (row.identifierName == exitCondition)
{
conditionMet = true
}
else
{
conditionMet = false
// Continue your processing
}
}
return payload]]></scripting:script>
</scripting:component>
</otherwise>
</choice>
</foreach>
</flow>
Try these and let us know if you need more help.
There is no component to break the foreach loop. If you only want to process till the success condition, then you could use MEL to execute the foreach on a sublist like this:
<foreach collection="#[payload<condition>]" />
...
</foreach>
You can also use loops in Groovy.
Example. In a payload i have some HashMap -
def nap = payload.Result
for (v in nap)
{
if (v.Name == '!!! а папка')
{
assert v.Id != null
flowVars['folderid'] = v.Id
payload = 'recived Id of folder object is '+v.Id
break
}
}
return payload
Related
Hi I would like to seek for assistance for a DataWeave usage scenario.
I need to check if a variable for a card name exists (the card name is dynamic and cannot forsee beforehand).
If the variable already exists, then append current payload to that card name variable;
Else create the variable with the current payload
The problem is I do not have idea on how to refer to a variable with dynamic name.
I can save the current card name to a variable say "cardName", but how can I refer to the variable in DataWeave code afterwards?
Pseudoly below is what I would like to achieve
May I seek for advice on the way to achieve it?
You can access the vars using below notation
vars[dynamic variable]
As I do not know how your flow looks like and assuming you have a payload,
{
"data": [
{
"cardName": "cardName1",
"dataToMap": "first data"
},
{
"cardName": "cardName2",
"dataToMap": "2nd data"
},
{
"cardName": "cardName1",
"dataToMap": "2nd data for card name 1"
}
]
}
You can loop through the payload.data (using for each) and you can map it as
%dw 2.0
output application/java
var varName = payload.cardName
---
if (vars[varName] != null)
vars[varName] ++ "** **" ++ payload.dataToMap
else
payload.dataToMap
and have a set variable with name set with #[****] to dynamically choose the variable.
End result of this will have two vars with name cardName1 and cardName2 and their corresponding value will be "first data** **2nd data for card name 1" and "2nd data", respectively.
Here is an example flow of dynamically naming variables using a Set Variable component inside a For-Each loop.
This is a good way to persist data after the For-Each loop exits, since the payload resets to the payload before the For-Each scope is called.
<mule xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd">
<flow name="dynamic-varsFlow" >
<scheduler doc:name="Scheduler" >
<scheduling-strategy >
<fixed-frequency frequency="10000"/>
</scheduling-strategy>
</scheduler>
<set-payload value='#[output application/json
var someInput = {
"data": [
{
"cardName": "cardName1",
"dataToMap": "first data"
},
{
"cardName": "cardName2",
"dataToMap": "2nd data"
},
{
"cardName": "cardName1",
"dataToMap": "2nd data for card name 1"
}
]
}
---
someInput.data]' doc:name="Set Payload" />
<foreach doc:name="For Each" >
<set-variable value='#[output application/json
var varName = payload.cardName as String
---
(if (vars[varName] != null)
vars[varName] ++ "** **" ++ payload.dataToMap
else
payload.dataToMap
)]' doc:name="Set Variable" variableName="#[payload.cardName]"/>
</foreach>
<foreach doc:name="Copy_of_For Each" >
<logger level="INFO" doc:name="Logger"
message="#[output application/json --- {varName: payload.cardName, varValue: vars[payload.cardName]}]"/>
</foreach>
</flow>
</mule>
`
Here are the log messages for each of the dynamically named vars inside the second For-Each loop. Notice there are two new vars dynamically named from the initial Set Payload data. In a more real-world flow, the payload would be read from the output of the Event Source, such as from an HTTP Connector or DB Connector:
Here is the payload and vars after the second For-Each scope is exited. The payload reverts back to it's initial value, but the changes made inside the first For-Each scope persist in the two vars:
You should use a Java map instead and use cardName as the key.
ok i might have not understood the ask 100% but i did a pretty simple flow to illustrate setting a variable dynamically if that variable doesnt exist. hope that helps you upto some extent. Here is how my complete flow looks like
in the set payload i am setting a dummy payload:
in the transform message next i declared a variable card which doesnt exists until then as you can see in the flow
and finally in the last set payload i am setting the payload with whatever vars.card hold
and i get the response back when i test my API
now if you want to dynamically declare the variable name with some value i believe that can be done too.
I have an XML Message with two parameters in it which I use to call a REST service endpoint. However, if any of them are a certain value I would like to change them before my call, for example
<Interface Server="ABC" Server2="DEF"/>
If any of those have the value "ABC" it should always be replaced with "BC" and in my call to the REST service I would send param1="BC" and param2="DEF" in the above example.
I was thinking of a Choice router and check if Server is "ABC" then set a flow-variable param1="BC" but then I realized I would have to do the same again for Server2 if that one is "ABC" ...and that feels like.. it must be an easier way to achieve this?
Am I right? Could I use some clever MEL or XPATH3 expression to always substitue the values to "BC" if any of them are "ABC"?
Regards
You can try the following configuration:
<enricher doc:name="Message Enricher">
<dw:transform-message doc:name="Transform Message">
<dw:set-payload><![CDATA[%dw 1.0
%output application/java
%var evaluation = "ABC"
%var substitution = "BC"
%function substitute(serverVal)(
serverVal when serverVal != evaluation otherwise substitution
)
---
payload.Interface.# mapObject {
($$): substitute($)
}
]]></dw:set-payload>
</dw:transform-message>
<enrich source="#[payload.Server]" target="#[variable:param1]"/>
<enrich source="#[payload.Server2]" target="#[variable:param2]"/>
</enricher>
Regardless how many attribute in your XML source, you just need to add the enricher element accordingly.
For example, you have a new XML source: <Interface Server="ABC" Server2="DEF" Server3="ABC"/>
Then you only need to add: <enrich source="#[payload.Server3]" target="#[variable:param3]"/> to set the new variable.
Notes: DataWeave is one of the EE features. For CE, you can replace it with other transformer, for example: Groovy. In the example below, the payload is in form of String. The original application/xml format is transformed to String using byte-array-to-string-transformer.
<scripting:component doc:name="Groovy">
<scripting:script engine="Groovy"><![CDATA[def attributeMap = new XmlSlurper().parseText(payload).attributes()
attributeMap.each() {
it.value = it.value == "ABC" ? "BC" : it.value
}
payload = attributeMap]]></scripting:script>
</scripting:component>
I'm trying to do simple if else condition in expression component. After expression component I have logger. My query here is I'm not able to see the test1 but can able to view temp value in logger component. Why?
Same time, if I print test1 value in system.out.println. Getting the value, but why not in logger?.
<quartz:inbound-endpoint responseTimeout="10000" doc:name="Quartz" connector-ref="Quartz" jobName="Feedjob" repeatInterval="36000000" >
<quartz:event-generator-job groupName="Feedjob" jobGroupName="Feedjob"/>
</quartz:inbound-endpoint>
<s3:list-objects config-ref="Amazon_S3" doc:name="Amazon S3" bucketName="${amazon.BucketName}" prefix="master"/>
<foreach doc:name="For Each">
<set-variable variableName="TestValue" value="#[payload.getKey()]" doc:name="Variable"/>
<expression-component doc:name="Expression"><![CDATA[
sessionVars.temp = message.outboundProperties['MULE_CORRELATION_SEQUENCE'] ;
if ( sessionVars.temp == "2"){
sessionVars.test1 = sessionVars.temp ;
System.out.println(sessionVars.test1);
return message.payload;
}
else{
System.out.println(" No test");
return message.payload;
}
]]></expression-component>
<logger message="Payload**********temp:#[sessionVars.temp] test1: #[sessionVars.test1]" level="INFO" doc:name="Logger"/>
</foreach>
<logger message="Outside For each logger**********temp:#[sessionVars.temp] test1: #[sessionVars.test1]" level="INFO" doc:name="Logger"/>
It seems to be once after payload returning from expression component sessionVars.temp is set, but sessionVars.test1 diaappears. It is strange. Where I'm wrong?
Two ways to fix this:
use #[sessionVars['test1']] instead of #[sessionVars.test1] while accessing session variables.
declare the session variable sessionVars.test1=""; before the if block
The issue seems to be with the way variables are accessed with . operator. It only happens when the variable is declared in a component which doesn't create those variables the first time (in your case, the test1 variable is created only in the second foreach iteration and not in the first). If your condition had been if ( sessionVars.temp == "1"), you wouldn't face this issue.
Apparently mule had already fixed this in latest versions, 3.7 seems to be working as expected. I had the same issue in 3.5.
I tried to recreate your scenario in following way :-
<set-property propertyName="MULE_CORRELATION_SEQUENCE" value="2" doc:name="Property"/>
<expression-component doc:name="Expression"><![CDATA[
sessionVars.temp = message.outboundProperties['MULE_CORRELATION_SEQUENCE'] ;
if ( sessionVars.temp == "2"){
sessionVars.test1 = sessionVars.temp ;
System.out.println(sessionVars.test1);
return message.payload;
}
else{
System.out.println(" No test");
return message.payload;
}
]]></expression-component>
and I am able to get the value in logger :- INFO 2015-08-06 09:19:48,556 [[testcon].HTTP_Listener_Configuration1.worker.01] org.mule.api.processor.LoggerMessageProcessor: Payload**********temp:2 test1: 2
Make sure your outbound property MULE_CORRELATION_SEQUENCE is not null or contains value
I am wanting to invoke a function on payload which is a Java pojo to set a value.
<set-variable variableName="name" value="xyz" doc:name="Variable"/>
<foreach doc:name="For Each">
<expression-transformer expression="#[payload.setEventId(flowVars['name'])]" doc:name="Expression"/>
<logger message="#[payload.getEventId()]" level="INFO" doc:name="Logger"/>
</foreach>
But this is giving me error.
Root Exception stack trace:
org.mule.api.transformer.TransformerException: Expression Evaluator "null" with expression "payload.setEventId(flowVars['name'])" returned null but a value was required.
The expression-transformer replaces the current payload with the value returned by the expression. I'm guessing setEventId is void thus the expression returns null, hence the exception.
Use an expression-component instead:
<expression-component>payload.setEventId(flowVars['name'])</expression-component>
<set-variable variableName="tempId" value="8000" doc:name="Variable" />
<expression-component doc:name="Expression">
<![CDATA[
temp1 = message.payload.getRootElement().selectNodes('//palns/*');
foreach (plan1 : temp1){
plan1.selectSingleNode('planid').text = #[flowVars.tempId];
} ]]></expression-component>
Above is my code . I can't set flow variable value inside expression component.
if i hardcode my value like below plan1.selectSingleNode('planid').text = '4000';
It is working.
But I want my flow variables value . Any thoughts?
Just remove the expression wrapper #[] as its not needed in expression-component:
plan1.selectSingleNode('planid').text = flowVars.tempId;
<set-variable variableName="tempId" value="8000" doc:name="Variable" />
<expression-component doc:name="Expression">
<![CDATA[
temp1 = message.payload.getRootElement().selectNodes('//palns/*');
foreach (plan1 : temp1){
plan1.selectSingleNode('planid').text = flowVars.tempId;
} ]]></expression-component>
Expression component doesn't take #[]