A process graph is made up of nodes and transitions. For more information about the graph and its executional model, refer to ???.
Each node has a specific type. The node type determines what will happen when an execution arrives in the node at runtime. jBPM has a set of preimplemented node types that you can use. Alternatively, you can write custom code for implementing your own specific node behaviour.
Each node has 2 main responsibilities: First, it can execute plain java code. Typically the plain java code relates to the function of the node. E.g. creating a few task instances, sending a notification, updating a database,... Secondly, a node is responsible for propagating the process execution. Basically, each node has the following options for propagating the process execution:
jBPM contains --as any workflow and BPM engine-- a set of preimplemented node types that have a specific documented configuration and behaviour. But the unique thing about jBPM and the Graph Oriented Programming foundation is that we open up the model for developers. Developers can write their own node behaviour very easy and use it in a process.
That is where traditional workflow and BPM systems are much more closed. They usually supply a fixed set of node types (called the process language). Their process language is closed and the executional model is hidden in the runtime environment. Research of workflow patterns has shown that any process language is not powerfull enough. We have decided for a simple model and allow developers to write their own node types. That way the JPDL process language is open ended.
Next, we discuss the most important node types of JPDL.
A task node represents one or more tasks that are to be performed by humans. So when execution arrives in a task node, task instances will be created in the task lists of the workflow participants. After that, the node will behave as a wait state. So when the users perform their task, the task completion will trigger the resuming of the execution. In other words, that leads to a new signal being called on the token.
A state is a bare-bones wait state. The difference with a task node is that no task instances will be created in any task list. This can be usefull if the process should wait for an external system. E.g. upon entry of the node (via an action on the node-enter event), a message could be sent to the external system. After that, the process will go into a wait state. When the external system send a response message, this can lead to a token.signal(), which triggers resuming of the process execution.
Actually there are 2 ways to model a decision. The distinction between the two is based on *who* is making the decision. Should the decision made by the process (read: specified in the process definition). Or should an external entity provide the result of the decision.
When the decision is to be taken by the process, a decision node should be used. There are basically 2 ways to specify the decision criteria. Simplest is by adding condition elements on the transitions. Conditions are EL expressions or beanshell scripts that return a boolean.
At runtime the decision node will FIRST loop over its leaving transitions THAT HAVE a condition specified. It will evaluate those transitions first in the order as specified in the xml. The first transition for which the conditions resolves to 'true' will be taken. If all transitions with a condition resolve to false, the default transition (the first in the XML) is taken.
Another approach is to use an expression that returns the name of the transition to take. With the 'expression' attribute, you can specify an expression on the decision that has to resolve to one of the leaving transitions of the decision node.
Next aproach is the 'handler' element on the decision, that element can be used to specify an implementation of the DecisionHandler interface can be specified on the decision node. Then the decision is calculated in a java class and the selected leaving transition is returned by the decide-method of the DecisionHandler implementation.
When the decision is taken by an external party (meaning: not part of the process
definition), you should use multiple transitions leaving a state or wait state node.
Then the leaving transition can be provided in the external trigger that resumes execution
after the wait state is finished. E.g. Token.signal(String transitionName)
and TaskInstance.end(String transitionName)
.
A fork splits one path of execution into multiple concurrent paths of execution. The default fork behaviour is to create a child token for each transition that leaves the fork, creating a parent-child relation between the token that arrives in the fork.
The default join assumes that all tokens that arrive in the join are children of the same parent. This situation is created when using the fork as mentioned above and when all tokens created by a fork arrive in the same join. A join will end every token that enters the join. Then the join will examine the parent-child relation of the token that enters the join. When all sibling tokens have arrived in the join, the parent token will be propagated over the (unique!) leaving transition. When there are still sibling tokens active, the join will behave as a wait state.
The type node serves the situation where you want to write your own code in a node. The nodetype node expects one subelement action. The action is executed when the execution arrives in the node. The code you write in the actionhandler can do anything you want but it is also responsible for propagating the execution.
This node can be used if you want to use a JavaAPI to implement some functional logic that is important for the business analyst. By using a node, the node is visible in the graphical representation of the process. For comparison, actions --covered next-- will allow you to add code that is invisible in the graphical representation of the process, in case that logic is not important for the business analyst.