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 :

  1. 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.
  2. 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.
  3. 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