The main expressions

This section describes the core expressions used to define a process [and its subprocesses].

process-definition

The definition of a process takes place within a <process-definition> expression.

<define> and <workflow-definition> are aliases for <process-definition>.

lambda process definitions

...

participant

The simplest flow could the one consisting of only a participant, perhaps you. A participant is for the engine (and for a flow definition therefore) just a name, a reference that is looked up in a participant map, perhaps a 'directory' would have been better suited for this abstraction.

The participant expression takes a main attribute called 'ref' (short for 'reference').


    <participant ref="role-alpha" />

This small flow snippet will be interpreted by OpenWFE engine as "dispatch current workitem to participant referenced as 'role-alpha'. Wait for the workitem that will perhaps come back.


    <participant ref="role-alpha" timeout="2h" />

If the participant behind the name 'role-alpha' doesn't reply after at least 2 hours, pass the workitem to the next (or implicitely terminates, if the participant is the only expression, the body, of the flow).


    <participant ref="role-alpha" timeout="no" />

Setting the timeout to "no" will preserve the participant from being timed out.

You can set the default timeout for all participants by setting the parameter 'defaultExpressionTimeout' to another value than the default 7 days. You have to edit the file etc/engine/engine-configuration.xml to do this :


    <!-- 
	the expressionPool takes care of the workflow instances

	the parameter 'cacheSize' indicates how many of the least recently used 
	expressions are cached by the expression pool ? 
    -->
    <service 
	name="expressionPool"
	class="openwfe.org.engine.impl.expool.CachedExpressionPool"
    >
	<param>
	    <param-name>cacheSize</param-name>
	    <param-value>700</param-value>
	</param>
	<param>
	    <param-name>purgeFrequency</param-name>
	    <param-value>10m</param-value>
	</param>
	<param>
	    <param-name>whenFrequency</param-name>
	    <param-value>40s</param-value>
	</param>
            <!-- stating how much time a when expression should wait before
                 testing its conditional child again -->
    </service>

All the implementations of ExpressionPool support this 'defaultExpressionTimeout' parameter.

Since OpenWFE 1.7.0, the 'participant' expression (as well as the 'when' expression) also look for the value of the variable '__timeout__' for determining their own timeout. Thus the priority order for setting the timeout is :

  • The participant expression's timeout attribute, like in

    
        <participant field-ref="__target__" timeout="3h30m" />
    
    

  • The '__timeout__' variable (set at any reachable level)

    
        <sequence>
            <set variable="__timeout__" value="3d" />
            <participant field-ref="__target__" />
        </sequence>
    
    

    Note that this variable may be set in the local scope, in the process definition scope ('/__timeout__'), or at the engine's level ('//__timeout__'). This latter level influencing the whole 'participant' (and 'when') expressions that didn't explicitely stated their timeout by the means of the eponymous attribute.

  • Then the timeout may be set in the participant-map, as as parameter of the participant declaration, like in

    
        <participant name="role-accountant">
            <param>
                <param-name>dispatcherClass</param-name>
                <param-value>openwfe.org.engine.impl.dispatch.SocketDispatcher</param-value>
            </param>
            <param>
                <param-name>host</param-name>
                <param-value>127.0.0.1</param-value>
            </param>
            <param>
                <param-name>port</param-name>
                <param-value>7008</param-value>
            </param>
            <param>
                <param-name>workItemCoder</param-name>
                <param-value>xmlCoder</param-value>
            </param>
            <param>
                <param-name>timeout</param-name>
                <param-value>1w</param-value>
                    <!-- one week -->
            </param>
        </participant>
    
    

  • Finally, if none of the previous 'method' yielded a value, the expression pool is consulted (and especially its 'expressionTimeOut' parameter). If this parameter is not set (in the etc/engine/engine-configuration.xml or programmatically for an embedded engine), the default timeout for an expression pool (and thus for an engine) is set to '7d' (seven days).

If the timeout yielded is equal to '-1', 'no', 'none' or 'never'. The expression will not time out.

Upon timing out a participant, the engine will resume the flow with the workitem as it was given to the participant. But the field '__timed_out__' will be added to the workitem and set to 'true'.


    <sequence>
        <participant ref="alice" />
        <if>
            <equals field-value="__timed_out__" other-value="true" />
            <participant ref="eve" />
        </if>
        <participant ref="bob" />
    </sequence>

In this example, the participant 'eve' will be part of the flow if 'alice' did not reply in time.

indirect references

The participant expression not only accepts the 'ref' attribute, you can also use the 'field-ref' or the 'variable-ref' attributes, like their names imply, they seek their values in the fields of the incoming workitem or in the variables available in the current scope.


    <sequence>
        <participant field-ref="__target__" />
        <participant variable-ref="vtarget" />
    </sequence>

The first participant in the example references a participant whose name is stored in the field named '__target__' of the workitem as it reaches the participant expression. The second participant will seek its reference in the content of the variable named 'vtarget'. See the 'set' expression and the iterators to learn how to get variables to work for you.


    <participant ref="${field:toto}_${index}" />

This last example combines a field reference and a variable reference without using 'field-ref' neither 'variable-ref'. This dollar notation is available to all expressions, not only to the participant expression.

Nested dollar notations are possible :


    <set variable="i" value="3" />
    <set field="sum" value="${i} + ${i} = ${c:sum('${i}', '${i}')}" />

will yield the value "3 + 3 = 6" in the field named "sum".

Forgetting a workitem

Setting the attribute 'forget' of the participant expression will make the engine resume the flow immediately, the workitem will be sent to the participant but the engine will not care about any reply. This may be very useful for sending simple messages (still workitems though) to participants (like notifications or reminders).


    <participant ref="role-alpha" forget="true" />

Filter definitions

As OpenWFE processes have tokens in the form of workitems, sometimes the payload of these items are worth a higher degree of control. A workflow definition may include as many filter definition as desired. A filter definition should be detailed after the body of the main flow and the bodies of the subprocesses.


    <filter-definition name="closed-1" type="closed" add="false" remove="false">
        <field regex="writable" permissions="rw" />
        <field regex="readonly" permissions="r" />
        <field regex="hidden" permissions="" />
    </filter-definition>

This filter named 'closed-1' is closed (everything that isn't explicitely allowed is not permitted), adding and removing fields are not allowed either and it sets permissions on three fields. These are designed with a regex (regular expression) so that


        <field regex="input_.*" permissions="rw" />

Means that all fields beginning with "input_" are readable and writable, so 'field' does mean setting the permissions on just one field only if the regex is a 'flat' one (no *, ., +, whatever).

To have any effect, a filter must be bound to a participant. This is done with the 'filter' attribute of the participant expression.


    <sequence>
        <participant ref="first-reviewer" filter="review-filter" />
        <participant ref="second-reviewer" filter="review-filter" />
        <participant ref="third-reviewer" />
        <participant ref="publisher" filter="publish-filter" />
    </sequence>

In this example flow, the 2 first reviewers receive filtered workitems, but not the 3rd one. The publisher also receives a filtered workitem, but another filter will 'control' it.

When dispatching a workitem to a participant with a filter set, the OpenWFE engine takes care of keeping a copy of the workitem. It then dispatches a mangled copy of the workitem where the hidden fields have been removed. When the workitem comes back from the participant, the engine 'enforces' the filter : cancelling any unallowed modification.

When dispatching, a copy of the filter is also sent in the payload of the workitem so that the worklist (then client) or apre handling the workitem is able to enforce / use the filter too. For example, the generic OpenWFE webclient takes care to show in read-only mode (no input field) read-only fields.

The 'participant' expression also allows you to route workitems to participants determined at runtime.


    <participant 
        default-ref="alice" 
        field-ref="__target__"
    />

This example will route workitems to the participant whose name can be found in the workitem field named '__target__'. If this field is not present, the workitem will be dispatched to the participant 'alice'.

Fallback with 'else-ref'

OpenWFE 1.5.2 introduces a new 'else-ref' attribute for the participant expression. Whenever dispatching to the regular participant fails, the participant listed int the 'else-ref' attribute are probed successively until delivery to one of them is successful.

Of course, you can also take advantage of the 'dollar notation' with the 'else-ref' attribute.


    <participant 
        ref="${watermark-agent}"
        else-ref="${failover-watermark-agent}, admin"
    />

Participant names are separated by commas. You could have a function returning such a list or a field containing such a list.


    <participant 
        ref="${watermark-agent}"
        else-ref="${call:determineFailoverAgents()}"
    />
    <participant 
        ref="${db-insertion-agent}"
        else-ref="${field:alternate-agents}"
    />

sequence

The 'sequence' expression is detailed at the section called “Pattern 1 (Sequence)”.

'sequence' drives the serial execution of the expressions composing a segment of process.

There's a cousin expression named 'cursor' which is a sequence where steps can be skipped. Its documentation is in the section called “cursor”.

concurrence

The 'concurrence' expression is detailed at the section called “Pattern 3 (Synchronization)”.

'concurrence' provides parallel execution of its 'children' expressions. It understands a certain synchronization syntax for reconciling the branches once there execution terminates.

iterator

The 'iterator' expression is detailed at the section called “Iterator”.

This expressions might be considered as OpenWFE's 'for' loop.

concurrent-iterator

The 'concurrent-iterator' expression is detailed at the section called “Pattern 13 (Multiple Instances with Design Time Knowledge)” (and in the subsequent patterns).

'concurrent-iterator' is very similar to 'iterator' but its branches are executed in parallel.

subprocess

As you may have seen, you can define subprocesses (just like methods or procedures) that are 'referenced' and executed. These subprocesses may be defined inside the calling process (or subprocess, yes you can nest them) or they may be external processes.

Here is an example of a flow calling another flow :


<?xml version="1.0" encoding="UTF-8"?> 
<process-definition 
    name="flow" 
    revision="1.1b"
>
    <description>
    Launching this flow will launch the subflow http://localhost:7079/subFlow__1.0.xml. 
    When this flow returns, the participant role-charly will receive the workitem.
    </description>

    <sequence>
        <subprocess 
            ref="secondaryEngine::http://localhost:7079/subFlow__1.1.xml"
            forget="false"
        />
        <participant ref="role-charly" />
    </sequence>

</process-definition>

This process definition calls another process definition. The attribute 'forget' is set to 'false', it means that before resuming to participant 'role-charly', the calling flow will wait for the called flow to terminate. If you didn't specify a prefix like "secondaryEngine::" and just give the URL of the flow to launch, this launch will be done in the same engine running the current flow. If you set 'forget' to 'true', the calling flow will continue and will not care about any subprocess output. You can also 'forget' the results of a subprocess defined inside your flow (or a parent subprocess).

Here is an example of a classical subprocess defined inside its main process :


<?xml version="1.0" encoding="UTF-8"?> 
<process-definition 
    name="flow" 
    revision="1.6"
>
    <description>
    A subprocess is some kind of 'routine' or 'procedure'. It allows you to decompose the activities in a flow.
    </description>

    <!-- main -->

    <sequence>
        <subprocess ref="review" />
        <participant ref="role-charly" />
        <subprocess ref="review" />
    </sequence>

    <!-- activity definitions -->

    <process-definition name="review">
        <concurrence>
            <participant ref="role-alpha" />
            <participant ref="role-bravo" />
        </concurrence>
    </process-definition>

</process-definition>

restricting the workitem passed to a subprocess

If nothing is specified, the newly launched subflow will use a copy of the workitem used by the parent flow. You can use the 'fields' attribute to the 'subprocess' expression to specify which fields from the 'parent workitem' should be copied / passed to the 'sub workitem'.


    <subprocess ref="review" fields=""/>

will launch the subprocess 'review' with a workitem that has no fields at all.


    <subprocess ref="check_issue" fields="field_echo, field_fox"/>

will launch the subprocess 'check_issue' with a workitem containing two fields : 'field_echo' and 'field_fox', copied from the parent workitem.


    <subprocess ref="resolve_issue" fields="issue_${i}_.*"/>

given that the variable 'i' is set to 4, then all fields starting with 'issue_4_' will be copied to the sub workitem.


    <subprocess ref="do_the_job" fields="alpha as f_bravo, bravo as f_alpha"/>

in this example, the sub workitem will have two fields 'f_bravo' and 'f_alpha', swapped from 'alpha' and 'bravo' fields of the parent workitem.


    <subprocess ref="sub_zero" fields="f1=(${field:customer_name})"/>

looks complicated, but this 'f1' field will receive as value the name of the customer surrounded by parenthesis. This '=' notation has a drawback : it only generates fields with string values. The 'as' notation copies field whatever type they have.

passing variables to subprocesses

You can bind variables as parameters of a subprocess call :



    <sequence>

        <participant ref="role-charly" />

        <subprocess ref="review">
            <set variable="reviewer1" value="role-alpha" />
            <set variable="reviewer2" value="role-bravo" />
        </subprocess>

    </sequence>

    <!-- activity definitions -->

    <process-definition name="review">
        <concurrence>
            <participant ref="${reviewer1}" />
            <participant ref="${reviewer2}" />
        </concurrence>
    </process-definition>


The variables will be bound only for the instance of the subprocess triggered by the subprocess call.

Attributes of the 'subprocess' tag are also passed as variables of the subprocess :



    <sequence>

        <participant ref="role-charly" />

        <subprocess 
            ref="review" 
            rev1="role-alpha" 
            rev2="role-bravo"
        />

    </sequence>

    <!-- activity definitions -->

    <process-definition name="review">
        <concurrence>
            <participant ref="${rev1}" />
            <participant ref="${rev2}" />
        </concurrence>
    </process-definition>


using the name of a subprocess directly

There is an elegant way of calling a subprocess :



    <sequence>

        <participant ref="role-charly" />

        <review />

    </sequence>

    <!-- activity definitions -->

    <process-definition name="review">
        <concurrence>
            <participant ref="role-alpha" />
            <participant ref="role-bravo" />
        </concurrence>
    </process-definition>


Of course, this is possible :



    <sequence>

        <participant ref="role-charly" />

        <review
            rev1="role-alpha" 
            rev2="role-bravo"
        />

    </sequence>

    <!-- activity definitions -->

    <process-definition name="review">
        <concurrence>
            <participant ref="${rev1}" />
            <participant ref="${rev2}" />
        </concurrence>
    </process-definition>


(beware, you should not use attributes like 'ref' or 'forget', they are reserved for the underlying subprocess ref expression).

Warning : flows using this notation will fail XSD validation.