JBoss jBPM 2.0 jPdl Reference Manual
This page describes the schema of the processdefinition.xml file.
The process archive
A process archive is a formal description of a business process. It is packaged
as a jar-formatted file, usually with an extension .par jBpm recognizes
that describing a process requires three types of data :
- a declarative description of the business process : in jPdl, this is expressed
in the
processdefinition.xml file. The bulk of this page explains
the schema of this file.
- programming logic : to attach programming logic to the process as it is described
in the processdefinition.xml, java-classes can be included in the process archive. All
classes in a process archive should be located in the
/classes subfolder.
- other resource files : as a client of the workflow engine, you might want
to include a variety of resource files in the process that can be used during runtime.
E.g. descriptions of forms that are associated with tasks to be performed by a person.
jBpm does not impose any restrictions on the type of resource files you want to include
in a process definition. See Other process archive files
Versioning mechanism
Basically, the versioning mechanism of jBpm comes down to the following principles :
- every time a process archive is deployed, a new process definition is created in the jBpm database
- at deployment, jBpm assigns a version number to the process definition. Process archives are considered
the same when the name of the process is the same. To assign the version number, jBpm takes 1 + (the highest
version number of the current process definitions with the same name). Or 1 if it is the first version.
From the jBpm-API you can ask for the latest process definition with a given name.
- ones a process instance (=one process execution) is started in a given definition, the process instance
will keep on executing within the same definition till its finished.
- this way every process can start in the latest definition and keeps on running in the same definition for
its complete lifetime.
- note that in jBpm it is even possible to version the programming logic associated with a process. By
including the classes in the process archive, jBpm will separate the classes per process definition.
The schema of processdefinition.xml
Document type definition
<!DOCTYPE process-definition PUBLIC
"-//jBpm/jBpm Mapping DTD 2.0 beta3//EN"
"http://jbpm.org/dtd/processdefinition-2.0-beta3.dtd">
|
the document type definition of processdefinition.xml
process-definition
<!ELEMENT process-definition ( description?,
swimlane*,
type*,
start-state,
( state |
milestone |
process-state |
decision |
fork |
join
)*,
end-state,
action* ) >
<!ATTLIST process-definition name CDATA #REQUIRED >
|
dtd fragment for process-definition
state
<!ELEMENT state ( description?, assignment?, action*, transition+ ) >
<!ATTLIST state name CDATA #REQUIRED >
|
dtd fragment for a state
In jBpm the term state has the same meaning as in finite state machines (FSM) or UML state diagrams.
The purpose of modelling a business process in jBpm is to create a software system. We consider
the process definition as a part of the software system being build. So the term state in jBpm is
to be interpreted from the viewpoint of the software system of which jBpm is a part.
State is the central concept of jBpm. When starting to model a process in jBpm, the first thing
to do is think about the states of the process. The states will serve as the basic framework of your
process.
There is a very specific reason why jBpm is centered around the state concept. That is because
state does not have a counterpart in programming languages. A software program is either running
or it is not. A business process typically has related pieces of programming logic that are
executed separately. jBpm allows to model these states between the related pieces of programming
logic.
jPdl also defines control flow between the states with transitions, decisions, forks, joins
and milestones. Note that the control flow defined between states. Wherever plain programming
logic is more appropriate, process developers can specify an action on a
process event. The phylisofy of jPdl is to extend java with state management and have the least
overlap with programming language capabilities. This differentiates jPdl from other business
process languages such as e.g. BPEL. Other process languages do not make that clear separation.
assignment
<ELEMENT assignment EMPTY >
<!ATTLIST assignment swimlane CDATA #IMPLIED
assignment (optional|required) #IMPLIED
authentication (optional|required|verify) #IMPLIED >
|
dtd fragment for an assignment
When a process execution arrives in a state, the workflow engine will wait until it is given
an external trigger (with the jBpm API method
ExecutionService.endOfState(...)).
Inside that method, jBpm will calculate the next state of the process instance.
So a state can be seen as a dependancy upon an external actor. The external actor can be a human,
a group or a system. There are numerous ways how states in a business process can relate to tasks which
basically can be divided into two groups :
Client based assignment
In this strategy, users of jBpm will manage the tasklists for their users themselves. jBpm is in
this case only used as an execution engine for finite state machines. jBpm's responsibility is
to calculate and keep track of the state of process executions, while it is the responsability
of the client to make sure that everybody knows what to do. Clients then typically want to find all
process instances (or tokens) in a given state.
Process based assignment
(this was to only assignment strategy supported before jbpm 2.0 beta3)
In this assignment strategy, jBpm will be responsible for assigning states to actors and
keep track of the tasklists. In jBpm, the progress during execution of a process is tracked
with a token. A token has a pointer to a state and to an actor. In jBpm an actor is always
referenced with a plain
java.lang.String. To specify how jBpm must assign tokens
to actors, several events are relevant. Let's go over them one by one :
Whenever a client starts
a new process instance or signals the end
of a state, jBpm starts calculating the next state for a process instance.
The first thing we want to mention about assignments is that the first parameter of these
API method calls is the actor. That parameter is used to specify on who's behalf the method is
executed.
In case of starting a new process instance, a root-token is created in the start-state. In
case of an endOfState, a tokenId needs to be specified as a parameter which is in some state.
Then, jBpm will start calculating the new state for the token. For simplicity we will ignore concurrency
for a moment. The token will travel over transitions and nodes until it arrives in a state
node. At that moment, the token needs to be assigned to an actor. After the
selection of the
actor, jBpm will associate the selected actor with the token and the invoked method (startProcessInstance
or endOfState) will return.
So when a token in a state has a reference to an actor, that means that execution of that process
instance is waiting for that actor to provide an external trigger to the jBpm engine.
In this case, a state in the process corresponds to a task for a user. jBpm
calculates the tasklist
by searching for all tokens that are assigned to the given actor. Now, the actor can
select a token from the list and signal the end of state again.
Assignment has to more attributes that are not yet covered : assignment and authentication.
The attribute assignment can have 2 values : optional and required.
required means that after execution has arrived in a state, jBpm will check if the
token is actually assigned to an actor. An AssignmentException is thrown if this is violated.
If assignment is optional (=default), jBpm is allowed to leave a token unassigned when it
arrives in a state. The attribute authentication specifies constraints for which
actor is allowed to signal the end of a state. The actor is specified with the actorId parameter
in the method endOfState.
optional (=default) means that its not required
to specify on who's behalf the end of state is signalled. required means that
an actor needs to be specified and verify means that the end of state may only be
signalled by the actor that is assigned to the token.
For more about coping with group assignments, see the faqs.
swimlane
<!ELEMENT swimlane ( description?, delegation? ) >
<!ATTLIST swimlane name CDATA #REQUIRED >
|
dtd fragment for swimlane
Typically, one person is responsible for multiple states in one process. In jBpm this is expressed
by creating a swimlane and assigning all states for that actor to the swimlane. A swimlane in a business
process can be seen as a role-name for an actor within the process. The jBpm interpretation of swimlane
corresponds to the term swimlane used in UML 1.5. The first time
when execution arrives in a state for a given swimlane, the
actor is calculated.
public interface AssignmentHandler {
String selectActor( AssignmentContext assignerContext );
}
|
the AssignmentHandler interface
The delegation tag within a swimlane references an implementation of AssignmentHandler.
Then that actor
is stored in a process variable with the same name as the swimlane. Next time when the process
arrives in a state for the given swimlane, the jBpm engine will notice the variable and assign
the token to the actor which was stored in the variable.
So swimlanes are defined on a process level
and states reference swimlanes in the
assignment.
The result of this calculation will be stored in a process variable with the same name
as the swimlane. So then when the next state is reached of the same swimlane, that state
is assigned to the same actor without using the AssignmentHandler again. Because the
relation between a swimlane and the actor is stored in a variable, it is possible to
manipulate that relation by updating the variable.
variable and type
<!ELEMENT type ( description?, (delegation|transient), variable* ) >
<!ATTLIST type java-type CDATA #IMPLIED >
<!ELEMENT transient EMPTY >
<!ELEMENT variable EMPTY >
<!ATTLIST variable name CDATA #REQUIRED >
|
dtd fragment for type and variable
Variables
A variable is a key-value pair associated with a process instance (=one process execution).
The key is a java.lang.String and the value is any
POJO's of any java type. So even
java-types, not know to jBpm can be used in process variables.
Variables store the context information of a process instance. Variables can be set in 3 ways :
- ExecutionService.startProcessInstance( String actorId, Long definitionId, Map variables, String transitionName )
- ExecutionService.endOfState( String actorId, Long tokenId, Map variables, String transitionName )
- ExecutionService.setVariables( String actorId, Long tokenId, Map variables )
- ExecutionContext.setVariable( String name, Object value )
- ExecutionService.getVariables( String actorId, Long tokenId )
- ExecutionContext.getVariable( String name )
When working with variables we have tried to mimic as much as possible the semantics of a java.util.Map
thoughout the jBpm-API, . This means that a variable is only instantiated when it is inserted (read: set)
and that any java-type can be used as value.
Type
A type specifies how jBpm should store the value of a variable in the database. jBpm has a text
field for storing values so the conversion between text and object is done with a Serializer :
public interface Serializer {
String serialize( Object object );
Object deserialize( String text );
}
|
the Serializer interface
A type can be seen as a reference to a Serializer. jBpm includes default Serializer implementations
for following java-types :
java.lang.String
java.lang.Long
java.lang.Double
|
by default supported java-types
Variable-type matching
Variables with a java-type that is supported by default do not have to be declared in the
processdefinition.xml. jBpm has a semi automatic typing mechanism : When a variable is
instantiated jBpm tries to calculate the type of the variable by examining the java-type
of the variable value. If the variable-value is a default supported java-type, that type
is used. Else, jBpm checks if the java-type of the variable-value corresponds with a type
declared in the processdefinition.xml (attribute java-type). Note that in this
matching jBpm also takes into consiceration the super-types of the variable-value. If no
such type is found, jBpm treats the variable as transient.
To avoid that jBpm has to execute the matching process, you can specify the variables in
each type.
Transient variables
Some variables do not need to be made persistent in the database. A variable 'to.email.address'
is being provided as a variable in an endOfState() call to the jBpm-API. The state has one leaving
transition with an action that sends an email to the given address. If that was the only usage
of the variable 'to.email.address' it would not have to be made persistent. For this purpose,
jBpm supports transient variables. Transient variables are not stored in the database and
can only be used inside the jBpm-API method call they're being supplied. In other words, the scope
of transient variables is a jBpm-API method call.
start-state
<!ELEMENT start-state ( description?, transition+ ) >
<!ATTLIST start-state name CDATA #REQUIRED
swimlane CDATA #IMPLIED >
|
dtd fragment for start-state
The start-state is the unique state in a process from which all process instances start.
Note that at process-instance-start-time you can already feed variables in the process. Another
important concept is that you can have multiple transitions leaving the start-state. In that
case, you need to specify which transition should be taken when you start a process instance.
milestone
<!ELEMENT milestone ( description?, action*, transition ) >
<!ATTLIST milestone name CDATA #REQUIRED>
|
dtd fragment for a milestone
A milestone is a special kind of state that can be used for synchronizing between
two concurrent paths of execution. A milestone can be used in a situation where
one path of execution needs to wait upon an event in another path of execution.
If the milestone was not reached, execution has to wait in the milestone state until
the other concurrent path of execution has reached the milestone. If the milestone
was already reached, the execution just passes through the milestone state.
For more information on milestones and a graphical animation, see
the workflow patterns.
A milestone state is related with one or more actions that signal the reaching of a
milestone. Those actions can be modelled in the process with the default ActionHandler :
org.jbpm.delegation.action.MilestoneReachedActionHandler. So the action
that signals to the jbpm engine that a milestone has been reached could be scheduled
like this in a processdefinition.xml :
...
<milestone name="theMilestone">
<transition to="stateAfterMilestone" />
</milestone>
...
<state name="stateBeforeReachingMilestone" swimlane="initiator">
<transition to="stateAfterReachingMilestone">
<action>
<delegation class="org.jbpm.delegation.action.MilestoneReachedActionHandler">theMilestone</delegation>
</action>
</transition>
</state>
...
|
modelling a milestone in the processdefinition.xml
process-state
<!ELEMENT process-state ( description?, delegation, action*, transition+ ) >
<!ATTLIST process-state name CDATA #REQUIRED>
|
dtd fragment for a process-state
A process state corresponds to the invocation of a super-process. The parent process starts a
sub-process when execution arrives in the process-state. The process remains in the
process-state for the duration of the sub-process. When the sub-process finishes, the process-state
is left.
decision
<!ELEMENT decision ( description?, delegation, action*, transition+ ) >
<!ATTLIST decision name CDATA #REQUIRED>
|
dtd fragment for a decision
A decision decides between multiple paths of execution which are exclusive. If you're a programmer,
just think of it as an if-then-else construct. Of course, a decision can have as many leaving transitions
as desired.
Note that a decision models a situation where the workflow engine decides which route to
take based upon the context (= variables) and perhaps some external resources. As an alternative,
you can model multiple transitions leaving a state. In that case, the jBpm client must decide
which of the leaving transitions to take by including the selected transition name as a parameter
in the endOfState method invocation.
fork
<!ELEMENT fork ( description?, delegation?, action*, transition+ ) >
<!ATTLIST fork name CDATA #REQUIRED
corresponding-join CDATA #IMPLIED>
|
dtd fragment for a fork
A fork spawns multiple concurrent paths of execution. It is possible to specify
custom fork behaviour with the ForkHandler interface. But the default behaviour
(when no delegation is specified in the fork) is that one child-token is spawned
for every leaving transition of the fork. So only for advanced exotic concurrency
you'll need to implement a custom ForkHandler.
Normally, a fork has a related join which defines a concurrency block. With the
default fork and join behaviour supports only strict nesting. The default fork and join
do not support transitions that cross concurrency block boundaries.
public interface ForkHandler {
void fork( ForkContext forkContext ) throws ExecutionException;
}
|
the ForkHandler interface
join
<!ELEMENT join ( description?, delegation?, action*, transition ) >
<!ATTLIST join name CDATA #REQUIRED
corresponding-fork CDATA #IMPLIED>
|
dtd fragment for a join
A fork joins multiple concurrent paths of execution. It is possible to specify
custom join behaviour with the JoinHandler interface. But the default behaviour
(when no delegation is specified in the join) is that all tokens that have been
spawned together in the corresponding fork. The last token to arrive in the join
will trigger the parent token to proceed over the leaving transition of the join.
So only for advanced exotic concurrency you'll need to implement a custom JoinHandler.
Constraint : a join can only have one leaving transition.
public interface JoinHandler {
void join( JoinContext joinContext ) throws ExecutionException;
}
|
the JoinHandler interface
end-state
<!ELEMENT end-state EMPTY >
<!ATTLIST end-state name CDATA #REQUIRED>
|
dtd fragment for end-state
A process-definition has exactly one end-state. When the execution of a process
instance arrives in the end-state, the process instance is finished.
transition
<!ELEMENT transition ( action* )>
<!ATTLIST transition name CDATA #IMPLIED
to CDATA #REQUIRED>
|
dtd fragment for a transition
Transitions specify directed connections between nodes. The transition
element should be put inside the node from which the transition leaves.
action
<!ELEMENT action ( delegation ) >
<!ATTLIST action event-type (process-start|process-end|
state-enter|state-leave|state-after-assignment|
milestone-enter|milestone-leave|
decision-enter|decision-leave|
fork-enter|fork-every-leave|
join-every-enter|join-leave|
transition) #IMPLIED>
|
dtd fragment for an action
An action is a piece of java code that can be executed by the workflow engine upon an event
during process execution.
The action is always defined as the child of an process-definition-element like
process-definition, state, transition, decision, ... . The parent element plus the
event-type define the exact moment during process execution when the action is executed. As you
can imagine, the possible event-type's of an action depend on the element by which the action is
contained. The event-type-names already suggest on which element they are applicable. You find
the complete story in the javadocs of
EventType.
public interface ActionHandler {
void execute( ExecutionContext executionContext );
}
|
the ActionHandler interface
delegation
<!ELEMENT delegation ( #PCDATA ) >
<!ATTLIST delegation class CDATA #REQUIRED>
|
dtd fragment for a delegation
TODO : explain the link between the containing element and the interface the delegation class has
to implement.
Other process archive files
When a process archive is deployed, the processdefinition.xml is parsed and that information
is stored in the jbpm database. All other files you add into the process archive are
stored in the db or in the file-system and associated with the process definition that is created.
As a client of the jbpm API you can access those files with ExecutionReadService.getFile( Long processDefinitionId,
String fileName )
The difference between a process archive and a process definition has to do with the
versioning mechanism