The expressions described here allows for routing the flow to a particular branch of their process definition.
Expressions routing the flow as well as expressions returning a boolean result (as the __result__ field of their workitem) are detailed here.
The 'if' expression is directly inspired of the Scheme (a Lisp dialect) if. This expression takes two or three direct children. The first one is the conditional clause, the second is the 'then' clause, and the third and optional child is the 'else' clause, any other direct children after the third will be ignored.
Here are a few examples of an 'if' expression in action :
<if>
<equals field-value="accepted" other-value="true" />
<participant ref="employee1" />
</if>
<if>
<equals variable-value="can_deliver" other-value="true" />
<participant ref="logistics" />
<participant ref="support" />
</if>
<if>
<greater-than variable-value="amount" other-value="1000" />
<participant ref="logistics" />
</if>
As you can see, the 'if' expression relies on conditional expressions like 'equals', 'not-equals', 'greater-than' and the like to operate. But you can use a short version of 'if' with its 'test' attribute :
<if
test="${field:dept} == logistics"
>
<participant ref="employee1" />
</if>
<if
test="${f:rank} != officer"
>
<participant ref="soldier_mess" />
<participant ref="of_mess" />
</if>
<if test="blue == fuscia" >
<participant ref="impossible" />
<participant ref="i_will_get_the_workitem" />
</if>
The shortcut 'if' expression understands '==' and '!=' operators. It compares the two string values on the left and the right of the operator (it trims them before) to decide. This is very simple but can be very effective at increasing the readability of a process definition.
Of course, it's possible not to have an operator (!= or ==), the test attribute will then simply determine if its value evaluates to true :
<if test="${accepted}">
<participant ref="direction" />
</if>
<if test="true">
<participant ref="always_see_the_workitem" />
</if>
A case awaits as children a serie of boolean expressions followed by a classic expressions (like 'participant'). As soon as a boolean expression evals to true, the expression that immediately follows is evaluated and then the case expressions terminates.
<case>
<defined field-value="budget" />
<participant ref="team-leader" />
<equals field-value="budget" other-value="next year" />
<sequence>
<participant ref="team-supervisor" />
<participant ref="unit-buyer" />
</sequence>
<!-- else -->
<participant ref="unit-supervisor" />
</case>
In this example, if the field budget is present and contains the string "next year", the participant 'team-supervisor' and then the participant "unit-buyer" will receive the workitem. If the field 'budget' is present but has another value, the workitem will go to the 'unit-supervisor' participant.
In the following example, if the workitem has a field named 'supplier' and this field holds the value 'acme corp.', the workitem will be dispatched to the participant 'alpha'. Else, it will go the participant 'bravo'.
<if>
<equals field-value="supplier" other-value="acme corp." />
<participant ref="alpha" />
<participant ref="bravo" />
</if>
other examples :
<equals field-value="supplier" other-field-value="customer" /> <equals variable-value="__iteration_value__" other-value="1" /> <equals variable-value="__iteration_value__" other-variable-value="size" /> <equals field-value="__subject__" other-variable-value="tempSubject" />
'Equals' works by binding a variable named '__boolean_result__' in the current scope.
The 'not' expression inverses the return value of an 'equals' expression.
<if>
<not><equals field-value="supplier" other-value="acme corp." /></not>
<participant ref="alpha" />
</if>
Thus, if the supplier is not 'acme corp.', the participant 'alpha' will be dispatched a workitem.
Not works by rebinding the variable named '__boolean_result__' with its inverted boolean value.
If the variable is not present (i.e. not bound), the flow will interrupt with an exception.
This conditional expression evaluates all of its children and will eval itself to true if all of the children evaluated to true.
<if>
<and>
<equals field-value="physical_test" other-value="true" />
<equals field-value="medical_test" other-value="true />
</and>
<!-- then -->
<subprocess ref="entry-test" />
</if>
'or' is another conditional expression which evaluates all of its children. It will return true if at least of them evaluates to true.
<if>
<or>
<equals field-value="physical_test" other-value="false" />
<equals field-value="medical_test" other-value="false />
</or>
<!-- then -->
<subprocess ref="reject-candidate" />
<!-- else -->
<subprocess ref="entry-test" />
</if>
Used to compare a value with another, it adopts the same syntax as the 'equals' expression but it adds and 'equals' attribute, which when set to 'true' or 'ok' means that the expression will read as 'greather than or equals to'.
<if>
<greater-than field-value="price" other-value="10000" equals="ok"/>
<participant ref="cfo" />
</if>
If the price field in the current workitem is greater than or equals to 10000, the participant 'CFO' will be dispatched the workitem.
This expression uses the same syntax as greater-than.
<if>
<lesser-than field-value="price" other-value="1000" />
<participant field-ref="unit-chief" />
<participant ref="cfo" />
</if>
If the price is lesser than 1000, the chief of the unit (the real participant can be found in the workitem's field named 'unit-chief') will receive the workitem. Else the participant named 'CFO' will.
<if>
<undefined field-value="supplier" />
<participant ref="alpha" />
<participant ref="bravo" />
</if>
If there is no field named 'supplier' in the 'travelling' workitem, it will be dispatched to 'alpha', else to 'bravo'.
It also works with 'variable-value="..."'
This expression is the inverse of 'undefined' and it works as you might expect. Here is a small example, demonstrating another feature, the timeout flag set by the engine :
<participant ref="lazy" />
<if>
<defined variable-value="__timed_out__" />
<participant ref="alpha" />
<participant ref="bravo" />
</if>
If the participant referenced as 'lazy' doesn't reply in time, the workitem will go to the participant 'alpha'. If 'lazy' did reply, 'bravo' will receive the workitem.
'when' is some kind of 'asynchronous if'. Unlike 'if', it doesn't have an else clause. When it is applied, the engine checks if the conditional clause is true.
<concurrence>
<when>
<equals variable-value="/alpha-flag" other-value="true" />
<participant ref="role-bravo" />
</when>
<sequence>
<participant ref="role-alpha" />
<set variable="/alpha-flag" value="true" />
<!--
<participant ref="role-charly" />
-->
</sequence>
</concurrence>
In this example (flow__1.15.xml), the participant 'role-bravo' will receive a copy of the workitem, after 'role-alpha' treated its workitem. It is important to realize that the workitem that bravo will receive will be a copy of the workitem as it was when the 'when' expression got applied.
Perhaps 'when' could have been considered a 'time related expression', but it fits well in the 'conditional expressions' section.
The frequency at which the workflow engine checks for the validity of a <when> expression is determined by the expression pool 'whenFrequency' parameter.
Out of the box, the standalone OpenWFE has it set to '40s' (40 seconds).
This parameter, like other engine parameters may be found in etc/engine/engine-configuration.xml for the service 'expressionPool'.
The default 'whenFrequency' is '2m' (2 minutes). This default frequency is used by the embedded OpenWFE engine out of the box. Of course, it may be manually set, as the embedded engines do accept a map of expool parameters in their constructor.
A when expression is subject to timeout, as well as a participant expression. After a certain amount time of time, the engine considers the condition will not evaluate to true and it will 'timeout' the when.
The timeout value is determined at 3 levels : expression, variables and engine (expression pool), ordered by priority.
<when timeout="3d10h">
<equals variable-value="/alpha-flag" other-value="true" />
<participant ref="role-bravo" />
</when>
After 3 days and 10 hours, the when will drop. That's an example of expression-level determination of timeout.
<sequence>
<set variable="__timeout__" value="3d10h" />
<when>
<equals variable-value="/alpha-flag" other-value="true" />
<participant ref="role-bravo" />
</when>
<sequence>
Is somehow equivalent (but all 'when' coming afterwards are affected as well). Note that you can set the '__timeout__' variable at engine level ('//__timeout__') or at process level ('/__timeout__') thus affecting more than your local 'when' (or 'participant' as it uses this variable as well).