Skip to end of metadata
Go to start of metadata

Prova offers a fairly unique combination of processing techniques, including workflows, reactive messaging, event processing and elements of functional programming. The idea is to give the users a great freedom for building distributed agents using a holistic approach that does not force them to a particular methodology.

The following syntactic and semantic instruments in Prova 3.0 capture the basics and offer unique advanced features for implementing workflows and business processes.

This Section discusses the Prova support for workflows, in particular, both simple and sophisticated logic-based predicate gateways.

Simple gateways

Prova includes a very simple mechanism (inspired by Join-calculus) for creating diverging branches (parallel gateway) and process join points (inclusive gateways). Consider the following simple workflow presented in the BPMN notation.


The following code from process_join.prova uses the built-in predicates init_join and join to implement this workflow.

This code prints:

In the example, all tasks are modeled as messages sent to self, i.e., the engine itself, and accepting the same message in the body of the same rule. This could be replaced by either in-body task execution, spawning a computation using spawn or sending a request message to an agent and accepting a reply with the results. Remember that rcvMsg does not block the current thread but waits for a message asynchronously, while keeping all the current data in the transparently created closure. Parallel branching is implemented by simply creating a predicate with multiple clauses and processing the relevant tasks asynchronously.

Now back to the init_join and join predicates. The former initializes a join (an inclusive gateway) by passing it (1) the conversation-id on which the join will be processed, (2) the exit predicate invoked when the join conditions are verified and (3) a list of join terms representing tokens that need to become available for the exit predicate of the join to fire. It is entirely possible to specify join terms containing arbitrary free variables, including anonymous variable '_'.

When processing reaches a point in the workflow where the join condition can be communicated to the engine, the code uses the join predicate that accepts the name of the agent, the conversation-id, the name of the join, and the concrete join term that corresponds to a newly available join token. When the engine detects that all join terms are available, it processes the exit goal for the predicate corresponding to the join name specified at initialization (see clauses for join_1 and join_2).

Event-driven gateways (edBPM)

Prova supports ebPBM-style of agent programming and, in fact, offers much more with the concept of reaction groups. One could argue that an event-driven gateway is a simplified version of a reaction group. Consider the following variation on the example in the previous section.

What changed here is the exclusive choice that the environment imposes on the process depending on whether the event run_a or run_b arrives first. Moreover, as soon as either of them is detected, the other alternative branch is immediately disabled so that only one branch can proceed.

The following modified first half of the above example shows how the event-driven gateway is implemented using a reaction group.

An OR reaction group gives the two message handlers for events run_a and run_b a collective behavior such that the group collectively waits for either of the two events to arrive. When either event arrives, due to the semantics of OR, the group as a whole is terminated so that whatever happens to be the alternative reaction, disappears. The @or annotated reaction is the exit channel from the group that is used for defining the semantics of the group as a whole. In this instance, we are actually not interested in common action logic so this reaction is not followed by anything else. However, there are sub-branches following either of the reactions annotated with @group (this annotation declares them to be part of the reaction group with a logical name g. So once run_a or run_b arrives, the group as a whole disappears but the closure of the fired branch continues as the only extension of the workflow. Note that we use a Prova OR reaction group to model an XOR situation.

The beauty of this approach is the linearity of extensible semantic variants that can be added to this specification, translating to the ease of authoring and maintainability of the design, be it workflow, event pattern, or more general reactive behavior. For example, the following modification imposes a 10 seconds timeout on the group, adds a @not annotation on the run_b channel and a timeout channel.

If run_a arrives before the timeout, it remains the only branch as in the previous case. If neither run_a nor _run_b arrive before the timeout, the timeout channel proceeds with the branch with task b. Prova 3.0 includes a large collection of annotations for reaction groups that really help with designing very sophisticated workflows.

Predicate gateways

It is easy to see why simple joins may be quite limiting. Imagine you need to analyze what join tokens are available to the join and make a decision using logical reasoning. Furthermore, if the final join conditions are not yet achieved, it would be great if we could re-initialize the join, if some other conditions are verified. There are known workflow patterns (for example, Structured Discriminator) cataloged by van-der-Aalst that benefit from such features.

The following example predicate_join_concurrent.prova uses the enhanced version of the same workflow framework complemented by built-in predicates init_predicate_join and predicate_join.

Labels: