BPMN 2.0 Constructs This chapter covers the BPMN 20 constructs supported by Activiti as well as custom extensions to the BPMN standard.
Custom extensions The BPMN 2.0 standard is a good thing for all parties involved. End-users don't suffer from a vendor lock-in that comes by depending on a proprietary solution. Frameworks, and particularly open-source frameworks such as Activiti, can implement a solution that has the same (and often better implemented ;-) features as those of a big vendor. Due to the BPMN 2.0 standard, the transition from such a big vendor solution towards Activiti is an easy and smooth path. The downside of a standard however, is the fact that it is always the result of many discussions and compromises between different companies (and often visions). As a developer reading the BPMN 2.0 XML of a process definition, sometimes it feels like certain constructs or way to do things are too cumbersome. Since Activiti puts ease of development as a top-priority, we introduced something called the 'Activiti BPMN extensions'. These 'extensions' are new constructs or ways to simplify certain constructs, that are not in the BPMN 2.0 specification. Although the BPMN 2.0 specification clearly states that it was made for custom extension, we make sure that: The prerequisite of such a custom extension is that there always must be a simple transformation to the standard way of doing things. So when you decide to use a custom extension, you don't have to be afraid that there is no way back. When using a custom extension, this is always clearly indicated by giving the new XML element, attribute, etc. the activiti: namespace prefix. The goal of these extensions is to eventually push them back into a next version of the BPMN specification, or at least trigger a discussion that can lead to a revision of that specific BPMN construct. So whether you want to use a custom extension or not, is completely up to you. Several factors will influence this decision (graphical editor usage, company policy, etc.). We only provide them since we believe that some points in the standard can be done simpler or more efficient. Feel free to give us (positive and/or negative) feedback on our extensions, or to post new ideas for custom extensions. Who knows, some day your idea might pop up in the specification!.
Events Events are used to model something that happens during the lifetime process. Events are always visualized as a circle. In BPMN 2.0, there exist two main event categories: catching or throwing event. Catching: when process execution arrives in the event, it will wait for a trigger to happen. The type of trigger is defined by the inner icon or the type declaration in the XML. Catching events are visually differentiated from a throwing event by the inner icon that is not filled (i.e. it is white). Throwing: when process execution arrives in the event, a trigger is fired. The type of trigger is defined by the inner icon or the type declaration in the XML. Throwing events are visually differentiated from a catching event by the inner icon that is filled with black.
Event Definitions Event definitions define the semantics of an event. Without an event definition, an event "does nothing special". For instance a start event without and event definition does not specify what exactly starts the process. If we add an event definition to the start event (like for instance a timer event definition) we declare what "type" of event starts the process (in the case of a timer event definition the fact that a certain point in time is reached).
Timer Event Definitions Timer events are events which are triggered by defined timer. They can be used as start event, intermediate event or boundary event Timer definition must have exactly one element from the following: timeDate. This format specifies fixed date in ISO 8601 format, when trigger will be fired. Example: <timerEventDefinition> <timeDate>2011-03-11T12:13:14</timeDate> </timerEventDefinition> timeDuration. To specify how long the timer should run before it is fired, a timeDuration can be specified as sub-element of timerEventDefinition. The format used is the ISO 8601 format (as required by the BPMN 2.0 specification). Example (interval lasting 10 days): <timerEventDefinition> <timeDuration>P10D</timeDuration> </timerEventDefinition> timeCycle. Specifies repeating interval, which can be useful for starting process periodically, or for sending multiple reminders for overdue user task. Time cycle element can be in two formats. First is the format of recurring time duration, as specified by ISO 8601 standard. Example (3 repeating intervals, each lasting 10 hours): <timerEventDefinition> <timeCycle>R3/PT10H</timeCycle> </timerEventDefinition> Additionally, you can specify time cycle using cron expressions, example below shows trigger firing every 5 minutes, starting at full hour: 0 0/5 * * * ? Please see tutorial for using cron expressions. Note: The first symbol denotes seconds, not minutes as in normal Unix cron. The recurring time duration is better suited for handling relative timers, which are calculated with respect to some particular point in time (e.g. time when user task was started), while cron expressions can handle absolute timers - which is particularly useful for timer start events. You can use expressions for the timer event definitions, by doing so you can influence the timer definition based on process variables. The process variables must contain the ISO 8601 (or cron for cycle type) string for appropriate timer type. <boundaryEvent id="escalationTimer" cancelActivity="true" attachedToRef="firstLineSupport"> <timerEventDefinition> <timeDuration>${duration}</timeDuration> </timerEventDefinition> </boundaryEvent> Note: timers are only fired when the job executor is enabled (i.e. jobExecutorActivate needs to be set to true in the activiti.cfg.xml, since the job executor is disabled by default).
Error Event Definitions Error events are events which are triggered by a defined error. Important note: a BPMN error is NOT the same as a Java exception. In fact, the two have nothing in common. BPMN error events are a way of modeling business exceptions. Java exceptions are handled in their own specific way. An error event definition references an error element. The following is an example of an error end event, referencing an error declaration: <endEvent id="myErrorEndEvent"> <errorEventDefinition errorRef="myError" /> </endEvent> An error event handler references the same error element to declare that it catches the error.
Signal Event Definitions Signal events are events which reference a named signal. A signal is an event of global scope (broadcast semantics) and is delivered to all active handlers. A signal event definition is declared using the signalEventDefinition element. The attribute signalRef references a signal element declared as a child element of the definitions root element. The following is an excerpt of a process where a signal event is thrown and caught by intermediate events. <definitions... > <!-- declaration of the signal --> <signal id="alertSignal" name="alert" /> <process id="catchSignal"> <intermediateThrowEvent id="throwSignalEvent" name="Alert"> <!-- signal event definition --> <signalEventDefinition signalRef="alertSignal" /> </intermediateThrowEvent> ... <intermediateCatchEvent id="catchSignalEvent" name="On Alert"> <!-- signal event definition --> <signalEventDefinition signalRef="alertSignal" /> </intermediateCatchEvent> ... </process> </definitions> The signalEventDefinitions reference the same signal element.
Throwing a Signal Event A signal can either be thrown by a process instance using a bpmn construct or programmatically using java API. The following methods on the org.activiti.engine.RuntimeService can be used to throw a signal programmatically: RuntimeService.signalEventReceived(String signalName); RuntimeService.signalEventReceived(String signalName, String executionId); The difference between signalEventReceived(String signalName); and signalEventReceived(String signalName, String executionId); is that the first method throws the signal globally to all subscribed handlers (broadcast semantics) and the second method delivers the signal to a specific execution only.
Catching a Signal Event A signal event can be caught by an intermediate catch signal event or a signal boundary event.
Querying for Signal Event subscriptions It is possible to query for all executions which have subscribed to a specific signal event: List<Execution> executions = runtimeService.createExecutionQuery() .signalEventSubscriptionName("alert") .list(); We could then use the signalEventReceived(String signalName, String executionId) method to deliver the signal to these executions.
Signal event scope By default, signals are broadcast process engine wide. This means that you can throw a signal event in a process instance, and other process instances with different process definitions can react on the occurrence of this event. However, sometimes it is wanted to react to a signal event only within the same process instance. A use case for example is a synchronization mechanism in the process instance, if two or more activities are mutually exclusive. To restrict the scope of the signal event, add the (non-BPMN 2.0 standard!) scope attribute to the signal event definition: <signal id="alertSignal" name="alert" activiti:scope"processInstance"/> The default value for this is attribute is "global".
Signal Event example(s) The following is an example of two separate processes communicating using signals. The first process is started if an insurance policy is updated or changed. After the changes have been reviewed by a human participant, a signal event is thrown, signaling that a policy has changed: This event can now be caught by all process instances which are interested. The following is an example of a process subscribing to the event. Note: it is important to understand that a signal event is broadcast to all active handlers. This means in the case of the example given above, that all instances of the process catching the signal would receive the event. In this case this is what we want. However, there are also situations where the broadcast behavior is unintended. Consider the following process: The pattern described in the process above is not supported by Activiti. The idea is that the error thrown while performing the "do something" task is caught by the boundary error event and would be propagated to the parallel path of execution using the signal throw event and then interrupt the "do something in parallel" task. So far Activiti would perform as expected. The signal would be propagated to the catching boundary event and interrupt the task. However, due to the broadcast semantics of the signal, it would also be propagated to all other process instances which have subscribed to the signal event. In this case, this might not be what we want. Note: the signal event does not perform any kind of correlation to a specific process instance. On the contrary, it is broadcast to all process instances. If you need to deliver a signal to a specific process instance only, perform correlation manually and use signalEventReceived(String signalName, String executionId) and the appropriate query mechanisms.
Message Event Definitions Message events are events which reference a named message. A message has a name and a payload. Unlike a signal, a message event is always directed at a single receiver. A message event definition is declared using the messageEventDefinition element. The attribute messageRef references a message element declared as a child element of the definitions root element. The following is an excerpt of a process where two message events is declared and referenced by a start event and an intermediate catching message event. <definitions id="definitions" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" targetNamespace="Examples" xmlns:tns="Examples"> <message id="newInvoice" name="newInvoiceMessage" /> <message id="payment" name="paymentMessage" /> <process id="invoiceProcess"> <startEvent id="messageStart" > <messageEventDefinition messageRef="newInvoice" /> </startEvent> ... <intermediateCatchEvent id="paymentEvt" > <messageEventDefinition messageRef="payment" /> </intermediateCatchEvent> ... </process> </definitions>
Throwing a Message Event As an embeddable process engine, activiti is not concerned with actually receiving a message. This would be environment dependent and entail platform-specific activities like connecting to a JMS (Java Messaging Service) Queue/Topic or processing a Webservice or REST request. The reception of messages is therefore something you have to implement as part of the application or infrastructure into which the process engine is embedded. After you have received a message inside your application, you must decide what to do with it. If the message should trigger the start of a new process instance, choose between the following methods offered by the runtime service: ProcessInstance startProcessInstanceByMessage(String messageName); ProcessInstance startProcessInstanceByMessage(String messageName, Map<String, Object> processVariables); ProcessInstance startProcessInstanceByMessage(String messageName, String businessKey, Map<String, Object> processVariables); These methods allow starting a process instance using the referenced message. If the message needs to be received by an existing process instance, you first have to correlate the message to a specific process instance (see next section) and then trigger the continuation of the wating execution. The runtime service offers the following methods for triggering an execution based on a message event subscription: void messageEventReceived(String messageName, String executionId); void messageEventReceived(String messageName, String executionId, HashMap<String, Object> processVariables);
Querying for Message Event subscriptions Activiti supports message start events and intermediate message events. In the case of a message start event, the message event subscription is associated with a particular process definition. Such message subscriptions can be queried using a ProcessDefinitionQuery: ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() .messageEventSubscription("newCallCenterBooking") .singleResult(); Since there can only be one process definition for a specific message subscription, the query always returns zero or one results. If a process definition is updated, only the newest version of the process definition has a subscription to the message event. In the case of an intermediate catch message event, the message event subscription is associated with a particular execution. Such message event subscriptions can be queried using a ExecutionQuery: Execution execution = runtimeService.createExecutionQuery() .messageEventSubscriptionName("paymentReceived") .variableValueEquals("orderId", message.getOrderId()) .singleResult(); Such queries are called correlation queries and usually require knowledge about the processes (in this case that there will be at most one process instance for a given orderId).
Message Event example(s) The following is an example of a process which can be started using two different messages: This is useful if the process needs alternative ways to react to different start events but eventually continues in a uniform way.
Start Events A start event indicates where a process starts. The type of start event (process starts on arrival of message, on specific time intervals, etc.), defining how the process is started is shown as a small icon in the visual representation of the event. In the XML representation, the type is given by the declaration of a sub-element. Start events are always catching: conceptually the event is (at any time) waiting until a certain trigger happens. In a start event, following Activiti-specific properties can be specified: initiator: identifies the variable name in which the authenticated user id will be stored when the process is started. Example: <startEvent id="request" activiti:initiator="initiator" /> The authenticated user must be set with the method IdentityService.setAuthenticatedUserId(String) in a try-finally block like this: try { identityService.setAuthenticatedUserId("bono"); runtimeService.startProcessInstanceByKey("someProcessKey"); } finally { identityService.setAuthenticatedUserId(null); } This code is baked into the Activiti Explorer application. So it works in combination with
None Start Event
Description A 'none' start event technically means that the trigger for starting the process instance is unspecified. This means that the engine cannot anticipate when the process instance must be started. The none start event is used when the process instance is started through the API by calling one of the startProcessInstanceByXXX methods. ProcessInstance processInstance = runtimeService.startProcessInstanceByXXX(); Note: a subprocess always has a none start event.
Graphical notation A none start event is visualized as a circle with no inner icon (i.e. no trigger type).
XML representation The XML representation of a none start event is the normal start event declaration, without any sub-element (other start event types all have a sub-element declaring the type). <startEvent id="start" name="my start event" />
Custom extensions for the none start event formKey: references to a form template that users have to fill in when starting a new process instance. More information can be found in the forms section Example: <startEvent id="request" activiti:formKey="org/activiti/examples/taskforms/request.form" />
Timer Start Event
Description A timer start event is used to create process instance at given time. It can be used both for processes which should start only once and for processes that should start in specific time intervals. Note: a subprocess cannot have a timer start event. Note: start timer event is scheduled as soon as process is deployed. There is no need to call startProcessInstanceByXXX, although calling start process methods is not restricted and will cause one more starting of the process at the time of startProcessInstanceByXXX Invocation. Note: when a new version of a process with a start timer event is deployed, the job corresponding with the previous timer will be removed. The reasoning is that normally it is not wanted to keep automatically starting new process instances of this old version of the process.
Graphical notation A none start event is visualized as a circle with clock inner icon.
XML representation The XML representation of a timer start event is the normal start event declaration, with timer definition sub-element. Please refer to timer definitions for configuration details. for details on configuration details. Example: process will start 4 times, in 5 minute intervals, starting on 11th march 2011, 12:13 <startEvent id="theStart"> <timerEventDefinition> <timeCycle>R4/2011-03-11T12:13/PT5M</timeCycle> </timerEventDefinition> </startEvent> Example: process will start once, on selected date <startEvent id="theStart"> <timerEventDefinition> <timeDate>2011-03-11T12:13:14</timeDate> </timerEventDefinition> </startEvent>
Message Start Event
Description A message start event can be used to start a process instance using a named message. This effectively allows us to select the right start event from a set of alternative start events using the message name. When deploying a process definition with one or more message start events, the following considerations apply: The name of the message start event must be unique across a given process definition. A process definition must not have multiple message start events with the same name. Activiti throws an exception upon deployment of a process definition such that two or more message start events reference the same message of if two or more message start events reference messages with the same message name. The name of the message start event must be unique across all deployed process definitions. Activiti throws an exception upon deployment of a process definition such that one or more message start events reference a message with the same name as a message start event already deployed by a different process definition. Process versioning: Upon deployment of a new version of a process definition, the message subscriptions of the previous version are cancelled. This is also true for message events that are not present in the new version. When starting a process instance, a message start event can be triggered using the following methods on the RuntimeService: ProcessInstance startProcessInstanceByMessage(String messageName); ProcessInstance startProcessInstanceByMessage(String messageName, Map<String, Object> processVariables); ProcessInstance startProcessInstanceByMessage(String messageName, String businessKey, Map<String, Object< processVariables); The messageName is the name given in the name attribute of the message element referenced by the messageRef attribute of the messageEventDefinition. The following considerations apply when starting a process instance: Message start events are only supported on top-level processes. Message start events are not supported on embedded sub processes. If a process definition has multiple message start events, runtimeService.startProcessInstanceByMessage(...) allows to select the appropriate start event. If a process definition has multiple message start events and a single none start event, runtimeService.startProcessInstanceByKey(...) and runtimeService.startProcessInstanceById(...) starts a process instance using the none start event. If a process definition has multiple message start events and no none start event, runtimeService.startProcessInstanceByKey(...) and runtimeService.startProcessInstanceById(...) throw an exception. If a process definition has a single message start event, runtimeService.startProcessInstanceByKey(...) and runtimeService.startProcessInstanceById(...) start a new process instance using the message start event. If a process is started from a call activity, message start event(s) are only supported if in addition to the message start event(s), the process has a single none start event the process has a single message start event and no other start events.
Graphical notation A message start event is visualized as a circle with a message event symbol. The symbol is unfilled, to visualize the catching (receiving) behavior.
XML representation The XML representation of a message start event is the normal start event declaration with a messageEventDefinition child-element: <definitions id="definitions" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" targetNamespace="Examples" xmlns:tns="Examples"> <message id="newInvoice" name="newInvoiceMessage" /> <process id="invoiceProcess"> <startEvent id="messageStart" > <messageEventDefinition messageRef="tns:newInvoice" /> </startEvent> ... </process> </definitions>
Signal Start Event
Description A signal start event can be used to start a process instance using a named signal. The signal can be 'fired' from within a process instance using the intermediary signal throw event or through the API (runtimService.signalEventReceivedXXX methods). In both cases, all process definitions that have a signal start event with the same name will be started. Note that in both cases, it is also possible to chose between a synchronous and asynchronous starting of the process instances. The signalName that must be passed in the API is the name given in the name attribute of the signal element referenced by the signalRef attribute of the signalEventDefinition.
Graphical notation A signal start event is visualized as a circle with a signal event symbol. The symbol is unfilled, to visualize the catching (receiving) behavior.
XML representation The XML representation of a message start event is the normal start event declaration with a messageEventDefinition child-element: <signal id="theSignal" name="The Signal" /> <process id="processWithSignalStart1"> <startEvent id="theStart"> <signalEventDefinition id="theSignalEventDefinition" signalRef="theSignal" /> </startEvent> <sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" /> <userTask id="theTask" name="Task in process A" /> <sequenceFlow id="flow2" sourceRef="theTask" targetRef="theEnd" /> <endEvent id="theEnd" /> </process>
Error Start Event
Description An error start event can be used to trigger an Event Sub-Process. An error start event cannot be used for starting a process instance. An error start event is always interrupting.
Graphical notation A error start event is visualized as a circle with an error event symbol. The symbol is unfilled, to visualize the catching (receiving) behavior.
XML representation The XML representation of an error start event is the normal start event declaration with an errorEventDefinition child-element: <startEvent id="messageStart" > <errorEventDefinition errorRef="someError" /> </startEvent>
End Events An end event signifies the end (of a path) of a (sub)process. An end event is always throwing. This means that when process execution arrives in the end event, a result is thrown. The type of result is depicted by the inner black icon of the event. In the XML representation, the type is given by the declaration of a sub-element.
None End Event
Description A 'none' end event means that the result thrown when the event is reached is unspecified. As such, the engine will not do anything extra besides ending the current path of execution.
Graphical notation A none end event is visualized as a circle with a thick border with no inner icon (no result type).
XML representation The XML representation of a none end event is the normal end event declaration, without any sub-element (other end event types all have a sub-element declaring the type). <endEvent id="end" name="my end event" />
Error End Event
Description When process execution arrives in an error end event, the current path of execution is ended and an error is thrown. This error can caught by a matching intermediate boundary error event. In case no matching boundary error event is found, an exception will be thrown.
Graphical notation An error end event is visualized as a typical end event (circle with thick border), with the error icon inside. The error icon is completely black, to indicate the throwing semantics.
XML representation And error end event is represented as an end event, with a errorEventDefinition child element. <endEvent id="myErrorEndEvent"> <errorEventDefinition errorRef="myError" /> </endEvent> The errorRef attribute can reference an error element that is defined outside the process: <error id="myError" errorCode="123" /> ... <process id="myProcess"> ... The errorCode of the error will be used to find the matching catching boundary error event. If the errorRef does not match any defined error, then the errorRef is used as a shortcut for the errorCode. This is an Activiti specific shortcut. More concretely, following snippets are equivalent in functionality. <error id="myError" errorCode="error123" /> ... <process id="myProcess"> ... <endEvent id="myErrorEndEvent"> <errorEventDefinition errorRef="myError" /> </endEvent> is equivalent with <endEvent id="myErrorEndEvent"> <errorEventDefinition errorRef="error123" /> </endEvent> Note that the errorRef must comply with the BPMN 2.0 schema, and must be a valid QName.
Cancel End Event [EXPERIMENTAL]
Description The cancel end event can only be used in combination with a bpmn transaction subprocess. When the cancel end event is reached, a cancel event is thrown which must be caught by a cancel boundary event. The cancel boundary event then cancels the transaction and triggers compensation.
Graphical notation A cancel end event visualized as a typical end event (circle with thick outline), with the cancel icon inside. The cancel icon is completely black, to indicate the throwing semantics.
XML representation A cancel end event is represented as an end event, with a cancelEventDefinition child element. <endEvent id="myCancelEndEvent"> <cancelEventDefinition /> </endEvent>
Boundary Events Boundary events are catching events that are attached to an activity (a boundary event can never be throwing). This means that while the activity is running, the event is listening for a certain type of trigger. When the event is caught, the activity is interrupted and the sequence flow going out of the event are followed. All boundary events are defined in the same way: <boundaryEvent id="myBoundaryEvent" attachedToRef="theActivity"> <XXXEventDefinition/> </boundaryEvent> A boundary event is defined with A unique identifier (process-wide) A reference to the activity to which the event is attached through the attachedToRef attribute. Note that a boundary event is defined on the same level as the activities to which they are attached (i.e. no inclusion of the boundary event inside the activity). An XML sub-element of the form XXXEventDefinition (e.g. TimerEventDefinition, ErrorEventDefinition, etc.) defining the type of the boundary event. See the specific boundary event types for more details.
Timer Boundary Event
Description A timer boundary event acts as a stopwatch and alarm clock. When an execution arrives in the activity where the boundary event is attached to, a timer is started. When the timer fires (e.g. after a specified interval), the activity is interrupted and the sequence flow going out of the timer boundary event are followed.
Graphical Notation A timer boundary event is visualized as a typical boundary event (i.e. circle on the border), with the timer icon on the inside.
XML Representation A timer boundary event is defined as a regular boundary event. The specific type sub-element is in this case a timerEventDefinition element. <boundaryEvent id="escalationTimer" cancelActivity="true" attachedToRef="firstLineSupport"> <timerEventDefinition> <timeDuration>PT4H</timeDuration> </timerEventDefinition> </boundaryEvent> Please refer to timer event definition for details on timer configuration. In the graphical representation, the line of the circle is dotted as you can see in this example above: A typical use case is sending an escalation email additionally but not interrupt the normal process flow. Since BPMn 2.0 there is the difference between the interrupting and non interrupting timer event. The interrupting is the default. The non-interrupting leads to the original activity is not interrupted but the activity stays there. Instead an additional executions is created and send over the outgoing transition of the event. In the XML representation, the cancelActivity attribute is set to false: <boundaryEvent id="escalationTimer" cancelActivity="false" attachedToRef="firstLineSupport"/> Note: boundary timer events are only fired when the job executor is enabled (i.e. jobExecutorActivate needs to be set to true in the activiti.cfg.xml, since the job executor is disabled by default).
Known issue with boundary events There is a known issue regarding concurrency when using boundary events of any type. Currently, it is not possible to have multiple outgoing sequence flow attached to a boundary event (see issue ACT-47). A solution to this problem is to use one outgoing sequence flow that goes to a parallel gateway.
Error Boundary Event
Description An intermediate catching error on the boundary of an activity, or boundary error event for short, catches errors that are thrown within the scope of the activity on which it is defined. Defining a boundary error event makes most sense on an embedded subprocess, or a call activity, as a subprocess creates a scope for all activities inside the subprocess. Errors are thrown by error end events. Such an error will propagate its parent scopes upwards until a scope is found on which a boundary error event is defined that matches the error event definition. When an error event is caught, the activity on which the boundary event is defined is destroyed, also destroying all current executions within (e.g. concurrent activities, nested subprocesses, etc.). Process execution continues following the outgoing sequence flow of the boundary event.
Graphical notation A boundary error event is visualized as a typical intermediate event (Circle with smaller circle inside) on the boundary, with the error icon inside. The error icon is white, to indicate the catch semantics.
Xml representation A boundary error event is defined as a typical boundary event: <boundaryEvent id="catchError" attachedToRef="mySubProcess"> <errorEventDefinition errorRef="myError"/> </boundaryEvent> As with the error end event, the errorRef references an error defined outside the process element: <error id="myError" errorCode="123" /> ... <process id="myProcess"> ... The errorCode is used to match the errors that are caught: If errorRef is omitted, the boundary error event will catch any error event, regardless of the errorCode of the error. In case an errorRef is provided and it references an existing error, the boundary event will only catch errors with the same error code. In case an errorRef is provided, but no error is defined in the BPMN 2.0 file, then the errorRef is used as errorCode (similar for with error end events).
Example Following example process shows how an error end event can be used. When the 'Review profitability' user task is completed by stating that not enough information is provided, an error is thrown. When this error is caught on the boundary of the subprocess, all active activities within the 'Review sales lead' subprocess are destroyed (even if 'Review customer rating' was not yet completed), and the 'Provide additional details' user task is created. This process is shipped as example in the demo setup. The process XML and unit test can be found in the org.activiti.examples.bpmn.event.error package.
Signal Boundary Event
Description An attached intermediate catching signal on the boundary of an activity, or boundary signal event for short, catches signals with the same signal name as the referenced signal definition. Note: contrary to other events like the boundary error event, a boundary signal event does not only catch signal events thrown from the scope it is attached to. On the contrary, a signal event has global scope (broadcast semantics) meaning that the signal can be thrown from any place, even from a different process instance. Note: contrary to other events like an error event, a signal is not consumed if it is caught. If you have two active signal boundary events catching the same signal event, both boundary events are triggered, event if they are part of different process instances.
Graphical notation A boundary signal event is visualized as a typical intermediate event (Circle with smaller circle inside) on the boundary, with the signal icon inside. The signal icon is white (unfilled), to indicate the catch semantics.
XML representation A boundary signal event is defined as a typical boundary event: <boundaryEvent id="boundary" attachedToRef="task" cancelActivity="true"> <signalEventDefinition signalRef="alertSignal"/> </boundaryEvent>
Example See section on signal event definitions.
Message Boundary Event
Description An attached intermediate catching message on the boundary of an activity, or boundary message event for short, catches messages with the same message name as the referenced message definition.
Graphical notation A boundary message event is visualized as a typical intermediate event (Circle with smaller circle inside) on the boundary, with the message icon inside. The message icon is white (unfilled), to indicate the catch semantics. Note that boundary message event can be both interrupting (right hand side) and non interrupting (left hand side).
XML representation A boundary message event is defined as a typical boundary event: <boundaryEvent id="boundary" attachedToRef="task" cancelActivity="true"> <messageEventDefinition messageRef="newCustomerMessage"/> </boundaryEvent>
Example See section on message event definitions.
Cancel Boundary Event [EXPERIMENTAL]
Description An attached intermediate catching cancel on the boundary of a transaction subprocess, or boundary cancel event for short, is triggered when a transaction is cancelled. When the cancel boundary event is triggered, it first interrupts all executions active in the current scope. Next, it starts compensation of all active compensation boundary events in the scope of the transaction. Compensation is performed synchronously, i.e. the boundary event waits before compensation is completed before leaving the transaction. When compensation is completed, the transaction subprocess is left using the sequence flow(s) running out of the cancel boundary event. Note: Only a single cancel boundary event is allowed for a transaction subprocess. Note: If the transaction subprocess hosts nested subprocesses, compensation is only triggered for subprocesses that have completed successfully. Note: If a cancel boundary event is placed on a transaction subprocess with multi instance characteristics, if one instance triggers cancellation, the boundary event cancels all instances.
Graphical notation A cancel boundary event is visualized as a typical intermediate event (Circle with smaller circle inside) on the boundary, with the cancel icon inside. The cancel icon is white (unfilled), to indicate the catching semantics.
XML representation A cancel boundary event is defined as a typical boundary event: <boundaryEvent id="boundary" attachedToRef="transaction" > <cancelEventDefinition /> </boundaryEvent> Since the cancel boundary event is always interrupting, the cancelActivity attribute is not required.
Compensation Boundary Event [EXPERIMENTAL]
Description An attached intermediate catching compensation on the boundary of an activity or compensation boundary event for short, can be used to attach a compensation handler to an activity. The compensation boundary event must reference a single compensation handler using a directed association. A compensation boundary event has a different activation policy from other boundary events. Other boundary events like for instance the signal boundary event are activated when the activity they are attached to is started. When the activity is left, they are deactivated and the corresponding event subscription is cancelled. The compensation boundary event is different. The compensation boundary event is activated when the activity it is attached to completes successfully. At this point, the corresponding subscription to the compensation events is created. The subscription is removed either when a compensation event is triggered or when the corresponding process instance ends. From this, it follows: When compensation is triggered, the compensation handler associated with the compensation boundary event is invoked the same number of times the activity it is attached to completed successfully. If a compensation boundary event is attached to an activity with multiple instance characteristics, a compensation event subscription is created for each instance. If a compensation boundary event is attached to an activity which is contained inside a loop, a compensation event subscription is created for each time the activity is executed. If the process instance ends, the subscriptions to compensation events are cancelled. Note: the compensation boundary event is not supported on embedded subprocesses.
Graphical notation A compensation boundary event is visualized as a typical intermediate event (Circle with smaller circle inside) on the boundary, with the compensation icon inside. The compensation icon is white (unfilled), to indicate the catching semantics. In addition to a compensation boundary event, the following figure shows a compensation handler associated with the boundary event using a unidirectional association:
XML representation A compensation boundary event is defined as a typical boundary event: <boundaryEvent id="compensateBookHotelEvt" attachedToRef="bookHotel" > <compensateEventDefinition /> </boundaryEvent> <association associationDirection="One" id="a1" sourceRef="compensateBookHotelEvt" targetRef="undoBookHotel" /> <serviceTask id="undoBookHotel" isForCompensation="true" activiti:class="..." /> Since the compensation boundary event is activated after the activity has completed successfully, the cancelActivity attribute is not supported.
Intermediate Catching Events All intermediate catching events events are defined in the same way: <intermediateCatchEvent id="myIntermediateCatchEvent" > <XXXEventDefinition/> </intermediateCatchEvent> An intermediate catching event is defined with A unique identifier (process-wide) An XML sub-element of the form XXXEventDefinition (e.g. TimerEventDefinition, etc.) defining the type of the intermediate catching event. See the specific catching event types for more details.
Timer Intermediate Catching Event
Description A timer intermediate event acts as a stopwatch. When an execution arrives in catching event activity, a timer is started. When the timer fires (e.g. after a specified interval), the sequence flow going out of the timer intermediate event is followed.
Graphical Notation A timer intermediate event is visualized as a intermediate catching event, with the timer icon on the inside.
XML Representation A timer intermediate event is defined as a intermediate catching event. The specific type sub-element is in this case a timerEventDefinition element. <intermediateCatchEvent id="timer"> <timerEventDefinition> <timeDuration>PT5M</timeDuration> </timerEventDefinition> </intermediateCatchEvent> See timer event definitions for configuration details.
Signal Intermediate Catching Event
Description An intermediate catching signal event catches signals with the same signal name as the referenced signal definition. Note: contrary to other events like an error event, a signal is not consumed if it is caught. If you have two active signal boundary events catching the same signal event, both boundary events are triggered, event if they are part of different process instances.
Graphical notation An intermediate signal catch event is visualized as a typical intermediate event (Circle with smaller circle inside), with the signal icon inside. The signal icon is white (unfilled), to indicate the catch semantics.
XML representation A signal intermediate event is defined as a intermediate catching event. The specific type sub-element is in this case a signalEventDefinition element. <intermediateCatchEvent id="signal"> <signalEventDefinition signalRef="newCustomerSignal" /> </intermediateCatchEvent>
Example See section on signal event definitions.
Message Intermediate Catching Event
Description An intermediate catching message event catches messages with a specified name.
Graphical notation An intermediate catching message event is visualized as a typical intermediate event (Circle with smaller circle inside), with the message icon inside. The message icon is white (unfilled), to indicate the catch semantics.
XML representation A message intermediate event is defined as a intermediate catching event. The specific type sub-element is in this case a messageEventDefinition element. <intermediateCatchEvent id="message"> <messageEventDefinition signalRef="newCustomerMessage" /> </intermediateCatchEvent>
Example See section on message event definitions.
Intermediate Throwing Event All intermediate throwing events events are defined in the same way: <intermediateThrowEvent id="myIntermediateThrowEvent" > <XXXEventDefinition/> </intermediateThrowEvent> An intermediate throwing event is defined with A unique identifier (process-wide) An XML sub-element of the form XXXEventDefinition (e.g. signalEventDefinition, etc.) defining the type of the intermediate throwing event. See the specific throwing event types for more details.
Intermediate Throwing None Event The following process diagram shows a simple example of an intermediate none event, which is often used to indicate some state achieved in the process. This can be a good hook to monitor some KPI's, basically by adding an execution listener <intermediateThrowEvent id="noneEvent"> <extensionElements> <activiti:executionListener class="org.activiti.engine.test.bpmn.event.IntermediateNoneEventTest$MyExecutionListener" event="start" /> </extensionElements> </intermediateThrowEvent> There you can add some own code to maybe send some event to your BAM tool or DWH. The engine itself doesn't do anything in that event, it just passes through.
Signal Intermediate Throwing Event
Description An intermediate throwing signal event throws a signal event for a defined signal. In Activiti, the signal is broadcast to all active handlers (i.e. all catching signal events). Signals can be published synchronous or asynchronous. In the default configuration, the signal is delivered synchronously. This means that the throwing process instance waits until the signal is delivered to all catching process instances. The catching process instances are also notified in the same transaction as the throwing process instance, which means that if one of the notified instances produces a technical error (throws an exception), all involved instances fail. A signal can also be delivered asynchronously. In that case it is determined which handlers are active at the time the throwing signal event is reached. For each active handler, an asynchronous notification message (Job) is stored and delivered by the JobExecutor.
Graphical notation An intermediate signal throw event is visualized as a typical intermediate event (Circle with smaller circle inside), with the signal icon inside. The signal icon is black (filled), to indicate the throw semantics.
XML representation A signal intermediate event is defined as a intermediate throwing event. The specific type sub-element is in this case a signalEventDefinition element. <intermediateThrowEvent id="signal"> <signalEventDefinition signalRef="newCustomerSignal" /> </intermediateThrowEvent> An asynchronous signal event would look like this: <intermediateThrowEvent id="signal"> <signalEventDefinition signalRef="newCustomerSignal" activiti:async="true" /> </intermediateThrowEvent>
Example See section on signal event definitions.
Compensation Intermediate Throwing Event [EXPERIMENTAL]
Description An intermediate throwing compensation event can be used to trigger compensation. Triggering compensation: Compensation can either be triggered for a designated activity or for the scope which hosts the compensation event. Compensation is performed through execution of the compensation handler associated with an activity. When compensation is thrown for an activity, the associated compensation handler is executed the same number of times the activity competed successfully. If compensation is thrown for the current scope, all activities withing the current scope are compensated, which includes activities on concurrent branches. Compensation is triggered hierarchically: if an activity to be compensated is a subprocess, compensation is triggered for all activities contained in the subprocess. If the subprocess has nested activities, compensation is thrown recursively. However, compensation is not propagated to the "upper levels" of the process: if compensation is triggered within a subprocess, it is not propagated to activities outside of the subprocess scope. The bpmn specification states that compensation is triggered for activities at "the same level of subprocess". In Activiti compensation is performed in reverse order of execution. This means that whichever activity completed last is compensated first, etc. The intermediate throwing compensation event can be used to compensate transaction subprocesses which competed successfully. Note: If compensation is thrown within a scope which contains a subprocess and the subprocess contains activities with compensation handlers, compensation is only propagated to the subprocess if it has completed successfully when compensation is thrown. If some of the activities nested inside the subprocess have completed and have attached compensation handlers, the compensation handlers are not executed if the subprocess containing these activities is not completed yet. Consider the following example: In this process we have two concurrent executions, one executing the embedded subprocess and one executing the "charge credit card" activity. Lets assume both executions are started and the first concurrent execution is waiting for a user to complete the "review bookings" task. The second execution performs the "charge credit card" activity and an error is thrown, which causes the "cancel reservations" event to trigger compensation. At this point the parallel subprocess is not yet completed which means that the compensation event is not propagated to the subprocess and thus the "cancel hotel reservation" compensation handler is not executed. If the user task (and thus the embedded subprocess) completes before the "cancel reservations" is performed, compensation is propagated to the embedded subprocess. Process variables: When compensating an embedded subprocess, the execution used for executing the compensation handlers has access to the local process variables of the subprocess in the state they were in when the subprocess completed execution. To achieve this, a snapshot of the process variables associated with the scope execution (execution created for executing the subprocess) is taken. Form this, a couple of implications follow: The compensation handler does not have access to variables added to concurrent executions created inside the subprocess scope. Process variables associated with executions higher up in the hierarchy, (for instance process variables associated with the process instance execution are not contained in the snapshot: the compensation handler has access to these process variables in the state they are in when compensation is thrown. A variable snapshot is only taken for embedded subprocesses, not for other activities. Current limitations: waitForCompletion="false" is currently unsupported. When compensation is triggered using the intermediate throwing compensation event, the event is only left, after compensation completed successfully. Compensation itself is currently performed by concurrent executions. The concurrent executions are started in reverse order in which the compensated activities completed. Future versions of activity might include an option to perform compensation sequentially. Compensation is not propagated to sub process instances spawned by call activities.
Graphical notation An intermediate compensation throw event is visualized as a typical intermediate event (Circle with smaller circle inside), with the compensation icon inside. The compensation icon is black (filled), to indicate the throw semantics.
Xml representation A compensation intermediate event is defined as a intermediate throwing event. The specific type sub-element is in this case a compensateEventDefinition element. <intermediateThrowEvent id="throwCompensation"> <compensateEventDefinition /> </intermediateThrowEvent> In addition, the optional argument activityRef can be used to trigger compensation of a specific scope / activity: <intermediateThrowEvent id="throwCompensation"> <compensateEventDefinition activityRef="bookHotel" /> </intermediateThrowEvent>
Sequence Flow
Description A sequence flow is the connector between two elements of a process. After an element is visited during process execution, all outgoing sequence flow will be followed. This means that the default nature of BPMN 2.0 is to be parallel: two outgoing sequence flow will create two separate, parallel paths of execution.
Graphical notation A sequence flow is visualized as an arrow going from the source element towards the target element. The arrow always points towards the target.
XML representation Sequence flow need to have a process-unique id, and a reference to an existing source and target element. <sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" />
Conditional sequence flow
Description A sequence flow can have a condition defined on it. When a BPMN 2.0 activity is left, the default behavior is to evaluate the conditions on the outgoing sequence flow. When a condition evaluates to true, that outgoing sequence flow is selected. When multiple sequence flow are selected that way, multiple executions will be generated and the process will be continued in a parallel way. Note: the above holds for BPMN 2.0 activities (and events), but not for gateways. Gateways will handle sequence flow with conditions in specific ways, depending on the gateway type.
Graphical notation A conditional sequence flow is visualized as a regular sequence flow, with a small diamond at the beginning. The condition expression is shown next to the sequence flow.
XML representation A conditional sequence flow is represented in XML as a regular sequence flow, containing a conditionExpression sub-element. Note that for the moment only tFormalExpressions are supported, Omitting the xsi:type="" definition will simply default to this only supported type of expressions. <sequenceFlow id="flow" sourceRef="theStart" targetRef="theTask"> <conditionExpression xsi:type="tFormalExpression"> <![CDATA[${order.price > 100 && order.price < 250}]]> </conditionExpression> </sequenceFlow> Currently conditionalExpressions can only be used with UEL, detailed info about these can be found in section Expressions. The expression used should resolve to a boolean value, otherwise an exception is thrown while evaluating the condition. The example below references data of a process variable, in the typical JavaBean style through getters. <conditionExpression xsi:type="tFormalExpression"> <![CDATA[${order.price > 100 && order.price < 250}]]> </conditionExpression> This example invokes a method that resolves to a boolean value. <conditionExpression xsi:type="tFormalExpression"> <![CDATA[${order.isStandardOrder()}]]> </conditionExpression> The Activiti distribution contains the following example process using value and method expressions (see org.activiti.examples.bpmn.expression):
Default sequence flow
Description All BPMN 2.0 tasks and gateways can have a default sequence flow. This sequence flow is only selected as the outgoing sequence flow for that activity if and only if none of the other sequence flow could be selected. Conditions on a default sequence flow are always ignored.
Graphical notation A default sequence flow is visualized as a regular sequence flow, with a 'slash' marker at the beginning.
XML representation A default sequence flow for a certain activity is defined by the default attribute on that activity. The following XML snippet shows for example an exclusive gateway that has as default sequence flow flow 2. Only when conditionA and conditionB both evaluate to false, will it be chosen as outgoing sequence flow for the gateway. <exclusiveGateway id="exclusiveGw" name="Exclusive Gateway" default="flow2" /> <sequenceFlow id="flow1" sourceRef="exclusiveGw" targetRef="task1"> <conditionExpression xsi:type="tFormalExpression">${conditionA}</conditionExpression> </sequenceFlow> <sequenceFlow id="flow2" sourceRef="exclusiveGw" targetRef="task2"/> <sequenceFlow id="flow3" sourceRef="exclusiveGw" targetRef="task3"> <conditionExpression xsi:type="tFormalExpression">${conditionB}</conditionExpression> </sequenceFlow> Which corresponds with the following graphical representation:
Gateways A gateway is used to control the flow of execution (or as the BPMN 2.0 describes, the tokens of execution). A gateway is capable of consuming or generating tokens. A gateway is graphically visualized as a diamond shape, with an icon inside. The icon shows the type of gateway.
Exclusive Gateway
Description An exclusive gateway (also called the XOR gateway or more technical the exclusive data-based gateway), is used to model a decision in the process. When the execution arrives at this gateway, all outgoing sequence flow are evaluated in the order in which they are defined. The sequence flow which condition evaluates to true (or which doesn't have a condition set, conceptually having a 'true' defined on the sequence flow) is selected for continuing the process. Note that the semantics of outgoing sequence flow is different to that of the general case in BPMN 2.0. While in general all sequence flow which condition evaluates to true are selected to continue in a parallel way, only one sequence flow is selected when using the exclusive gateway. In case multiple sequence flow have a condition that evaluates to true, the first one defined in the XML (and only that one!) is selected for continuing the process. If no sequence flow can be selected, an exception will be thrown.
Graphical notation An exclusive gateway is visualized as a typical gateway (i.e. a diamond shape) with an 'X' icon inside, referring to the XOR semantics. Note that a gateway without an icon inside defaults to an exclusive gateway. The BPMN 2.0 specification does not allow mixing the diamond with and without an X in the same process definition.
XML representation The XML representation of an exclusive gateway is straight-forward: one line defining the gateway and condition expressions defined on the outgoing sequence flow. See the section on conditional sequence flow to see which options are available for such expressions. Take for example the following model: Which is represented in XML as follows: <exclusiveGateway id="exclusiveGw" name="Exclusive Gateway" /> <sequenceFlow id="flow2" sourceRef="exclusiveGw" targetRef="theTask1"> <conditionExpression xsi:type="tFormalExpression">${input == 1}</conditionExpression> </sequenceFlow> <sequenceFlow id="flow3" sourceRef="exclusiveGw" targetRef="theTask2"> <conditionExpression xsi:type="tFormalExpression">${input == 2}</conditionExpression> </sequenceFlow> <sequenceFlow id="flow4" sourceRef="exclusiveGw" targetRef="theTask3"> <conditionExpression xsi:type="tFormalExpression">${input == 3}</conditionExpression> </sequenceFlow>
Parallel Gateway
Description Gateways can also be used to model concurrency in a process. The most straightforward gateway to introduce concurrency in a process model, is the Parallel Gateway, which allows to fork into multiple paths of execution or join multiple incoming paths of execution. The functionality of the parallel gateway is based on the incoming and outgoing sequence flow: fork: all outgoing sequence flow are followed in parallel, creating one concurrent execution for each sequence flow. join: all concurrent executions arriving at the parallel gateway wait in the gateway until an execution has arrived for each of the incoming sequence flow. Then the process continues past the joining gateway. Note that a parallel gateway can have both fork and join behavior, if there are multiple incoming and outgoing sequence flow for the same parallel gateway. In that case, the gateway will first join all incoming sequence flow, before splitting into multiple concurrent paths of executions. An important difference with other gateway types is that the parallel gateway does not evaluate conditions. If conditions are defined on the sequence flow connected with the parallel gateway, they are simply neglected.
Graphical Notation A parallel gateway is visualized as a gateway (diamond shape) with the 'plus' symbol inside, referring to the 'AND' semantics.
XML representation Defining a parallel gateway needs one line of XML: <parallelGateway id="myParallelGateway" /> The actual behavior (fork, join or both), is defined by the sequence flow connected to the parallel gateway. For example, the model above comes down to the following XML: <startEvent id="theStart" /> <sequenceFlow id="flow1" sourceRef="theStart" targetRef="fork" /> <parallelGateway id="fork" /> <sequenceFlow sourceRef="fork" targetRef="receivePayment" /> <sequenceFlow sourceRef="fork" targetRef="shipOrder" /> <userTask id="receivePayment" name="Receive Payment" /> <sequenceFlow sourceRef="receivePayment" targetRef="join" /> <userTask id="shipOrder" name="Ship Order" /> <sequenceFlow sourceRef="shipOrder" targetRef="join" /> <parallelGateway id="join" /> <sequenceFlow sourceRef="join" targetRef="archiveOrder" /> <userTask id="archiveOrder" name="Archive Order" /> <sequenceFlow sourceRef="archiveOrder" targetRef="theEnd" /> <endEvent id="theEnd" /> In the above example, after the process is started, two tasks will be created: ProcessInstance pi = runtimeService.startProcessInstanceByKey("forkJoin"); TaskQuery query = taskService.createTaskQuery() .processInstanceId(pi.getId()) .orderByTaskName() .asc(); List<Task> tasks = query.list(); assertEquals(2, tasks.size()); Task task1 = tasks.get(0); assertEquals("Receive Payment", task1.getName()); Task task2 = tasks.get(1); assertEquals("Ship Order", task2.getName()); When these two tasks are completed, the second parallel gateway will join the two executions and since there is only one outgoing sequence flow, no concurrent paths of execution will be created, and only the Archive Order task will be active. Note that a parallel gateway does not need to be 'balanced' (i.e. a matching number of incoming/outgoing sequence flow for corresponding parallel gateways). A parallel gateway will simply wait for all incoming sequence flow and create a concurrent path of execution for each outgoing sequence flow, not influenced by other constructs in the process model. So, the following process is legal in BPMN 2.0:
Inclusive Gateway
Description The Inclusive Gateway can be seen as a combination of an exclusive and a parallel gateway. Like an exclusive gateway you can define conditions on outgoing sequence flows and the inclusive gateway will evaluate them. But the main difference is that the inclusive gateway can take more than one sequence flow, like the parallel gateway. The functionality of the inclusive gateway is based on the incoming and outgoing sequence flow: fork: all outgoing sequence flow conditions are evaluated and for the sequence flow conditions that evaluate to true the flows are followed in parallel, creating one concurrent execution for each sequence flow. join: all concurrent executions arriving at the inclusive gateway wait in the gateway until an execution has arrived for each of the incoming sequence flows that have a process token. This is an important difference with the parallel gateway. So in other words, the inclusive gateway will only wait for the incoming sequence flows that will be executed. After the join, the process continues past the joining inclusive gateway. Note that an inclusive gateway can have both fork and join behavior, if there are multiple incoming and outgoing sequence flow for the same inclusive gateway. In that case, the gateway will first join all incoming sequence flows that have a process token, before splitting into multiple concurrent paths of executions for the outgoing sequence flows that have a condition that evaluates to true.
Graphical Notation A parallel gateway is visualized as a gateway (diamond shape) with the 'circle' symbol inside.
XML representation Defining an inclusive gateway needs one line of XML: <inclusiveGateway id="myInclusiveGateway" /> The actual behavior (fork, join or both), is defined by the sequence flows connected to the inclusive gateway. For example, the model above comes down to the following XML: <startEvent id="theStart" /> <sequenceFlow id="flow1" sourceRef="theStart" targetRef="fork" /> <inclusiveGateway id="fork" /> <sequenceFlow sourceRef="fork" targetRef="receivePayment" > <conditionExpression xsi:type="tFormalExpression">${paymentReceived == false}</conditionExpression> </sequenceFlow> <sequenceFlow sourceRef="fork" targetRef="shipOrder" > <conditionExpression xsi:type="tFormalExpression">${shipOrder == true}</conditionExpression> </sequenceFlow> <userTask id="receivePayment" name="Receive Payment" /> <sequenceFlow sourceRef="receivePayment" targetRef="join" /> <userTask id="shipOrder" name="Ship Order" /> <sequenceFlow sourceRef="shipOrder" targetRef="join" /> <inclusiveGateway id="join" /> <sequenceFlow sourceRef="join" targetRef="archiveOrder" /> <userTask id="archiveOrder" name="Archive Order" /> <sequenceFlow sourceRef="archiveOrder" targetRef="theEnd" /> <endEvent id="theEnd" /> In the above example, after the process is started, two tasks will be created if the process variables paymentReceived == false and shipOrder == true. In case only one of these process variables equals to true only one task will be created. If no condition evaluates to true and exception is thrown. This can be prevented by specifying a default outgoing sequence flow. In the following example one task will be created, the ship order task: HashMap<String, Object> variableMap = new HashMap<String, Object>(); variableMap.put("receivedPayment", true); variableMap.put("shipOrder", true); ProcessInstance pi = runtimeService.startProcessInstanceByKey("forkJoin"); TaskQuery query = taskService.createTaskQuery() .processInstanceId(pi.getId()) .orderByTaskName() .asc(); List<Task> tasks = query.list(); assertEquals(1, tasks.size()); Task task = tasks.get(0); assertEquals("Ship Order", task.getName()); When this task is completed, the second inclusive gateway will join the two executions and since there is only one outgoing sequence flow, no concurrent paths of execution will be created, and only the Archive Order task will be active. Note that an inclusive gateway does not need to be 'balanced' (i.e. a matching number of incoming/outgoing sequence flow for corresponding inclusive gateways). An inclusive gateway will simply wait for all incoming sequence flow and create a concurrent path of execution for each outgoing sequence flow, not influenced by other constructs in the process model.
Event-based Gateway
Description The Event-based Gateway allows to take a decision based on events. Each outgoing sequence flow of the gateway needs to be connected to an intermediate catching event. When process execution reaches an Event-based Gateway, the gateway acts like a wait state: execution is suspended. In addition, for each outgoing sequence flow, an event subscription is created. Note the sequence flows running out of an Event-based Gateway are different from ordinary sequence flows. These sequence flows are never actually "executed". On the contrary, they allow the process engine to determine which events an execution arriving at an Event-based Gateway needs to subscribe to. The following restrictions apply: An Event-based Gateway must have two or more outgoing sequence flows. An Event-based Gateway must only be to elements of type intermediateCatchEvent only. (Receive Tasks after an Event-based Gateway are not supported by Activiti.) An intermediateCatchEvent connected to an Event-based Gateway must have a single incoming sequence flow.
Graphical notation An Event-based Gateway is visualized as a diamond shape like other BPMN gateways with a special icon inside.
XML representation The XML element used to define an Event-based Gateway is eventBasedGateway.
Example(s) The following process is an example of a process with an Event-based Gateway. When the execution arrives at the Event-based Gateway, process execution is suspended. In addition, the process instance subscribes to the alert signal event and created a timer which fires after 10 minutes. This effectively causes the process engine to wait for ten minutes for a signal event. If the signal occurs within 10 minutes, the timer is cancelled and execution continues after the signal. If the signal is not fired, execution continues after the timer and the signal subscription is cancelled. <definitions id="definitions" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" targetNamespace="Examples"> <signal id="alertSignal" name="alert" /> <process id="catchSignal"> <startEvent id="start" /> <sequenceFlow sourceRef="start" targetRef="gw1" /> <eventBasedGateway id="gw1" /> <sequenceFlow sourceRef="gw1" targetRef="signalEvent" /> <sequenceFlow sourceRef="gw1" targetRef="timerEvent" /> <intermediateCatchEvent id="signalEvent" name="Alert"> <signalEventDefinition signalRef="alertSignal" /> </intermediateCatchEvent> <intermediateCatchEvent id="timerEvent" name="Alert"> <timerEventDefinition> <timeDuration>PT10M</timeDuration> </timerEventDefinition> </intermediateCatchEvent> <sequenceFlow sourceRef="timerEvent" targetRef="exGw1" /> <sequenceFlow sourceRef="signalEvent" targetRef="task" /> <userTask id="task" name="Handle alert"/> <exclusiveGateway id="exGw1" /> <sequenceFlow sourceRef="task" targetRef="exGw1" /> <sequenceFlow sourceRef="exGw1" targetRef="end" /> <endEvent id="end" /> </process> </definitions>
Tasks
User Task
Description A 'user task' is used to model work that needs to be done by a human actor. When the process execution arrives at such a user task, a new task is created in the task list of the user(s) or group(s) assigned to that task.
Graphical notation A user task is visualized as a typical task (rounded rectangle), with a small user icon in the left upper corner.
XML representation A user task is defined in XML as follows. The id attribute is required, the name attribute is optional. <userTask id="theTask" name="Important task" /> A user task can have also a description. In fact any BPMN 2.0 element can have a description. A description is defined by adding the documentation element. <userTask id="theTask" name="Schedule meeting" > <documentation> Schedule an engineering meeting for next week with the new hire. </documentation> The description text can be retrieved from the task in the standard Java way: task.getDescription()
Due Date Each task has a field, indicating the due date of that task. The Query API can be used to query for tasks that are due on, before or after a certain date. There is an activity extension which allows you to specify an expression in your task-definition to set the initial due date of a task when it is created. The expression should always resolve to a java.util.Date, java.util.String (ISO8601 formatted), ISO8601 time-duration (eg. PT50M) or null. For example, you could use a date that was entered in a previous form in the process or calculated in a previous Service Task. In case a time-duration is used, the due-date is calculated based on the current time, incremented by the given period. For example, when "PT30M" is used as dueDate, the task is due in thirty minutes from now. <userTask id="theTask" name="Important task" activiti:dueDate="${dateVariable}"/>
The due date of a task can also be altered using the TaskService or in TaskListeners using the passed DelegateTask.
User assignment A user task can be directly assigned to a user. This is done by defining a humanPerformer sub element. Such a humanPerformer definition needs a resourceAssignmentExpression that actually defines the user. Currently, only formalExpressions are supported. <process ... > ... <userTask id='theTask' name='important task' > <humanPerformer> <resourceAssignmentExpression> <formalExpression>kermit</formalExpression> </resourceAssignmentExpression> </humanPerformer> </userTask> Only one user can be assigned as human performer to the task. In Activiti terminology, this user is called the assignee. Tasks that have an assignee are not visible in the task lists of other people and can be found in the so-called personal task list of the assignee instead. Tasks directly assigned to users can be retrieved through the TaskService as follows: List<Task> tasks = taskService.createTaskQuery().taskAssignee("kermit").list(); Tasks can also be put in the so-called candidate task list of people. In that case, the potentialOwner construct must be used. The usage is similar to the humanPerformer construct. Do note that it is required to define for each element in the formal expression to specify if it is a user or a group (the engine cannot guess this). <process ... > ... <userTask id='theTask' name='important task' > <potentialOwner> <resourceAssignmentExpression> <formalExpression>user(kermit), group(management)</formalExpression> </resourceAssignmentExpression> </potentialOwner> </userTask> Tasks defines with the potential owner construct, can be retrieved as follows (or a similar TaskQuery usage as for the tasks with an assignee): List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("kermit"); This will retrieve all tasks where kermit is a candidate user, i.e. the formal expression contains user(kermit). This will also retrieve all tasks that are assigned to a group where kermit is a member of (e.g. group(management), if kermit is a member of that group and the Activiti identity component is used). The groups of a user are resolved at runtime and these can be managed through the IdentityService. If no specifics are given whether the given text string is a user or group, the engine defaults to group. So the following would be the same as when group(accountancy) was declared. <formalExpression>accountancy</formalExpression>
Activiti extensions for task assignment It is clear that user and group assignments are quite cumbersome for use cases where the assignment is not complex. To avoid these complexities, custom extensions on the user task are possible. assignee attribute: this custom extension allows to directly assign a user task to a given user. <userTask id="theTask" name="my task" activiti:assignee="kermit" /> This is exactly the same as using a humanPerformer construct as defined above. candidateUsers attribute: this custom extension allows to make a user a candidate for a task. <userTask id="theTask" name="my task" activiti:candidateUsers="kermit, gonzo" /> This is exactly the same as using a potentialOwner construct as defined above. Note that it is not required to use the user(kermit) declaration as is the case with the potential owner construct, since the attribute can only be used for users. candidateGroups attribute: this custom extension allows to make a group a candidate for a task. <userTask id="theTask" name="my task" activiti:candidateGroups="management, accountancy" /> This is exactly the same as using a potentialOwner construct as defined above. Note that it is not required to use the group(management) declaration as is the case with the potential owner construct, since the attribute can only be used for groups. candidateUsers and candidateGroups can both be defined on the same user task. Note: Although Activiti provides an identity management component, which is exposed through the IdentityService, no check is done whether a provided user is known by the identity component. This allows Activiti to integrate with existing identity management solutions when it is embedded into an application. In case the previous approaches are not sufficient, it is possible to delegate to custom assignment logic using a task listener on the create event: <userTask id="task1" name="My task" > <extensionElements> <activiti:taskListener event="create" class="org.activiti.MyAssignmentHandler" /> </extensionElements> </userTask> The DelegateTask that is passed to the TaskListener implementation, allows to set the assignee and candidate-users/groups: public class MyAssignmentHandler implements TaskListener { public void notify(DelegateTask delegateTask) { // Execute custom identity lookups here // and then for example call following methods: delegateTask.setAssignee("kermit"); delegateTask.addCandidateUser("fozzie"); delegateTask.addCandidateGroup("management"); ... } } When using Spring it is possible to use the custom assignment attributes as described in the section above, and delegate to a Spring bean using a task listener with an expression that listens to task create events. In the following example, the assignee will be set by calling the findManagerOfEmployee on the ldapService Spring bean. The emp parameter that is passed, is a process variable>. <userTask id="task" name="My Task" activiti:assignee="${ldapService.findManagerForEmployee(emp)}"/> This also works similar for candidate users and groups: <userTask id="task" name="My Task" activiti:candidateUsers="${ldapService.findAllSales()}"/> Note that this will only work if the return type of the invoked methods is String or Collection<String> (for candidate users and groups): public class FakeLdapService { public String findManagerForEmployee(String employee) { return "Kermit The Frog"; } public List<String> findAllSales() { return Arrays.asList("kermit", "gonzo", "fozzie"); } }
Script Task
Description A script task is an automatic activity. When a process execution arrives at the script task, the corresponding script is executed.
Graphical Notation A script task is visualized as a typical BPMN 2.0 task (rounded rectangle), with a small 'script' icon in the top-left corner of the rectangle.
XML representation A script task is defined by specifying the script and the scriptFormat. <scriptTask id="theScriptTask" name="Execute script" scriptFormat="groovy"> <script> sum = 0 for ( i in inputArray ) { sum += i } </script> </scriptTask> The value of the scriptFormat attribute must be a name that is compatible with the JSR-223 (scripting for the Java platform). By default JavaScript is included in every JDK and as such doesn't need any additional jars. If you want to use another (JSR-223 compatible) scripting engine, it is sufficient to add the corresponding jar to the classpath and use the appropriate name. For example, the Activiti unit tests often use Groovy because the syntax is pretty similar to that of Java. Do note that the Groovy scripting engine is bundled with the groovy-all jar. Before version 2.0, the scripting engine was part of the regular Groovy jar. As such, one must now add following dependency: <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>2.x.x<version> </dependency>
Variables in scripts All process variables that are accessible through the execution that arrives in the script task, can be used within the script. In the example, the script variable 'inputArray' is in fact a process variable (an array of integers). <script> sum = 0 for ( i in inputArray ) { sum += i } </script> It's also possible to set process variables in a script, simply by calling execution.setVariable("variableName", variableValue). By default, no variables are stored automatically (Note: before Activiti 5.12 this was the case!). It is possible to automatically store any variable defined in the script (eg. sum in the example above) by setting the property autoStoreVariables on the scriptTask to true. However, the best practice is not to do this and use an explicit execution.setVariable() call, as on some recent versions of the JDK auto storing of variables does not work for some scripting languages. See this link for more details. <scriptTask id="script" scriptFormat="JavaScript" activiti:autoStoreVariables="false"> The default of this parameter is false, meaning that if the parameter is omitted from the script task definition, all the declared variables will only exist during the duration of the script. Example on how to set a variable in a script: <script> def scriptVar = "test123" execution.setVariable("myVar", scriptVar) </script> Note: the following names are reserved and cannot be used as variable names: out, out:print, lang:import, context, elcontext.
Script results The return value of a script task can be assigned to an already existing or to a new process variable by specifying the process variable name as a literal value for the 'activiti:resultVariable' attribute of a script task definition. Any existing value for a specific process variable will be overwritten by the result value of the script execution. When not specifying a result variable name, the script result value gets ignored. <scriptTask id="theScriptTask" name="Execute script" scriptFormat="juel" activiti:resultVariable="myVar"> <script>#{echo}</script> </scriptTask> In the above example, the result of the script execution (the value of the resolved expression '#{echo}') is set to the process variable named 'myVar' after the script completes.
Java Service Task
Description A Java service task is used to invoke an external Java class.
Graphical Notation A service task is visualized as a rounded rectangle with a small gear icon in the top-left corner.
XML representation There are 4 ways of declaring how to invoke Java logic: Specifying a class that implements JavaDelegate or ActivityBehavior Evaluating an expression that resolves to a delegation object Invoking a method expression Evaluating a value expression To specify a class that is called during process execution, the fully qualified classname needs to be provided by the 'activiti:class' attribute. <serviceTask id="javaService" name="My Java Service Task" activiti:class="org.activiti.MyJavaDelegate" /> See the implementation section for more details on how to use such a class. It is also possible to use an expression that resolves to an object. This object must follow the same rules as objects that are created when the activiti:class attribute is used (see further). <serviceTask id="serviceTask" activiti:delegateExpression="${delegateExpressionBean}" /> Here, the delegateExpressionBean is a bean that implements the JavaDelegate interface, defined in for example the Spring container. To specify a UEL method expression that should be evaluated, use attribute activiti:expression. <serviceTask id="javaService" name="My Java Service Task" activiti:expression="#{printer.printMessage()}" /> Method printMessage (without parameters) will be called on the named object called printer. It's also possible to pass parameters with an method used in the expression. <serviceTask id="javaService" name="My Java Service Task" activiti:expression="#{printer.printMessage(execution, myVar)}" /> Method printMessage will be called on the object named printer. The first parameter passed is the DelegateExecution, which is available in the expression context by default available as execution. The second parameter passed, is the value of the variable with name myVar in the current execution. To specify a UEL value expression that should be evaluated, use attribute activiti:expression. <serviceTask id="javaService" name="My Java Service Task" activiti:expression="#{split.ready}" /> The getter method of property ready, getReady (without parameters), will be called on the named bean called split. The named objects are resolved in the execution's process variables and (if applicable) in the Spring context.
Implementation To implement a class that can be called during process execution, this class needs to implement the org.activiti.engine.delegate.JavaDelegate interface and provide the required logic in the execute method. When process execution arrives at this particular step, it will execute this logic defined in that method and leave the activity in the default BPMN 2.0 way. Let's create for example a Java class that can be used to change a process variable String to uppercase. This class needs to implement the org.activiti.engine.delegate.JavaDelegate interface, which requires us to implement the execute(DelegateExecution) method. It's this operation that will be called by the engine and which needs to contain the business logic. Process instance information such as process variables and other can be accessed and manipulated through the DelegateExecution interface (click on the link for a detailed Javadoc of its operations). public class ToUppercase implements JavaDelegate { public void execute(DelegateExecution execution) throws Exception { String var = (String) execution.getVariable("input"); var = var.toUpperCase(); execution.setVariable("input", var); } } Note: there will be only one instance of that Java class created for the serviceTask it is defined on. All process-instances share the same class instance that will be used to call execute(DelegateExecution). This means that the class must not use any member variables and must be thread-safe, since it can be executed simultaneously from different threads. This also influences the way Field injection is handled. The classes that are referenced in the process definition (i.e. by using activiti:class) are NOT instantiated during deployment. Only when a process execution arrives for the first time at the point in the process where the class is used, an instance of that class will be created. If the class cannot be found, an ActivitiException will be thrown. The reasoning for this is that the environment (and more specifically the classpath) when you are deploying is often different from the actual runtime environment. For example when using ant or the business archive upload in Activiti Explorer to deploy processes, the classpath does not contain the referenced classes. [INTERNAL: non-public implementation classes] It is also possible to provide a class that implements the org.activiti.engine.impl.pvm.delegate.ActivityBehavior interface. Implementations have then access to the more powerful ActivityExecution that for example also allows to influence the control flow of the process. Note however that this is not a very good practice, and should be avoided as much as possible. So, it is advised to use the ActivityBehavior interface only for advanced use cases and if you know exactly what you're doing.
Field Injection It's possible to inject values into the fields of the delegated classes. The following types of injection are supported: Fixed string values Expressions If available, the value is injected through a public setter method on your delegated class, following the Java Bean naming conventions (e.g. field firstName has setter setFirstName(...)). If no setter is available for that field, the value of private member will be set on the delegate. SecurityManagers in some environments don't allow modifying private fields, so it's safer to expose a public setter-method for the fields you want to have injected. Regardless of the type of value declared in the process-definition, the type of the setter/private field on the injection target should always be org.activiti.engine.delegate.Expression. The following code snippet shows how to inject a constant value into a field. Field injection is supported when using the 'class' attribute. Note that we need to declare a 'extensionElements' XML element before the actual field injection declarations, which is a requirement of the BPMN 2.0 XML Schema. <serviceTask id="javaService" name="Java service invocation" activiti:class="org.activiti.examples.bpmn.servicetask.ToUpperCaseFieldInjected"> <extensionElements> <activiti:field name="text" stringValue="Hello World" /> </extensionElements> </serviceTask> The class ToUpperCaseFieldInjected has a field text which is of type org.activiti.engine.delegate.Expression. When calling text.getValue(execution), the configured string value Hello World will be returned. Alternatively, for longs texts (e.g. an inline e-mail) the 'activiti:string' sub element can be used: <serviceTask id="javaService" name="Java service invocation" activiti:class="org.activiti.examples.bpmn.servicetask.ToUpperCaseFieldInjected"> <extensionElements> <activiti:field name="text"> <activiti:string> Hello World </activiti:string> </activiti:field> </extensionElements> </serviceTask> To inject values that are dynamically resolved at runtime, expressions can be used. Those expressions can use process variables, or Spring defined beans (if Spring is used). As noted in Service Task Implementation, an instance of the Java class is shared among all process-instances in a service task. To have dynamic injection of values in fields, you can inject value and method expressions in a org.activiti.engine.delegate.Expression which can be evaluated/invoked using the DelegateExecution passed in the execute method. <serviceTask id="javaService" name="Java service invocation" activiti:class="org.activiti.examples.bpmn.servicetask.ReverseStringsFieldInjected"> <extensionElements> <activiti:field name="text1"> <activiti:expression>${genderBean.getGenderString(gender)}</activiti:expression> </activiti:field> <activiti:field name="text2"> <activiti:expression>Hello ${gender == 'male' ? 'Mr.' : 'Mrs.'} ${name}</activiti:expression> </activiti:field> </ extensionElements> </ serviceTask> The example class below uses the injected expressions and resolves them using the current DelegateExecution. Full code and test can be found in org.activiti.examples.bpmn.servicetask.JavaServiceTaskTest.testExpressionFieldInjection public class ReverseStringsFieldInjected implements JavaDelegate { private Expression text1; private Expression text2; public void execute(DelegateExecution execution) { String value1 = (String) text1.getValue(execution); execution.setVariable("var1", new StringBuffer(value1).reverse().toString()); String value2 = (String) text2.getValue(execution); execution.setVariable("var2", new StringBuffer(value2).reverse().toString()); } } Alternatively, you can also set the expressions as an attribute instead of a child-element, to make the XML less verbose. <activiti:field name="text1" expression="${genderBean.getGenderString(gender)}" /> <activiti:field name="text1" expression="Hello ${gender == 'male' ? 'Mr.' : 'Mrs.'} ${name}" /> Since the Java class instance is reused, the injection only happens once, when the serviceTask is called the first time. When the fields are altered by your code, the values won't be re-injected so you should treat them as immutable and don't make any changes to them.
Service task results The return value of a service execution (for service task using expression only) can be assigned to an already existing or to a new process variable by specifying the process variable name as a literal value for the 'activiti:resultVariable' attribute of a service task definition. Any existing value for a specific process variable will be overwritten by the result value of the service execution. When not specifying a result variable name, the service execution result value gets ignored. <serviceTask id="aMethodExpressionServiceTask" activiti:expression="#{myService.doSomething()}" activiti:resultVariable="myVar" /> In the example above, the result of the service execution (the return value of the 'doSomething()' method invocation on an object that is made available under the name 'myService' either in the process variables or as a Spring bean) is set to the process variable named 'myVar' after the service execution completes.
Handling exceptions When custom logic is executed, it is often required to catch certain business exceptions and handle them inside the surrounding process. Activiti provides different options to do that.
Throwing BPMN Errors It is possible to throw BPMN Errors from user code inside Service Tasks or Script Tasks. In order to do this, a special ActivitiException called BpmnError can be thrown in JavaDelegates, scripts, expressions and delegate expressions. The engine will catch this exception and forward it to an appropriate error handler, e.g., a Boundary Error Event or an Error Event Sub-Process. public class ThrowBpmnErrorDelegate implements JavaDelegate { public void execute(DelegateExecution execution) throws Exception { try { executeBusinessLogic(); } catch (BusinessException e) { throw new BpmnError("BusinessExceptionOccurred"); } } } The constructor argument is an error code, which will be used to determine the error handler that is responsible for the error. See Boundary Error Event for information on how to catch a BPMN Error. This mechanism should be used only for business faults that shall be handled by a Boundary Error Event or Error Event Sub-Process modeled in the process definition. Technical errors should be represented by other exception types and are usually not handled inside a process.
Exception Sequence Flow [INTERNAL: non-public implementation classes] Another option is to route process execution through another path in case some exception occurs. The following example shows how this is done. <serviceTask id="javaService" name="Java service invocation" activiti:class="org.activiti.ThrowsExceptionBehavior"> </serviceTask> <sequenceFlow id="no-exception" sourceRef="javaService" targetRef="theEnd" /> <sequenceFlow id="exception" sourceRef="javaService" targetRef="fixException" /> Here, the service task has two outgoing sequence flow, called exception and no-exception. This sequence flow id will be used to direct process flow in case of an exception: public class ThrowsExceptionBehavior implements ActivityBehavior { public void execute(ActivityExecution execution) throws Exception { String var = (String) execution.getVariable("var"); PvmTransition transition = null; try { executeLogic(var); transition = execution.getActivity().findOutgoingTransition("no-exception"); } catch (Exception e) { transition = execution.getActivity().findOutgoingTransition("exception"); } execution.take(transition); } }
Using an Activiti service from within a JavaDelegate For some use cases, it might be needed to use the Activiti services from within a Java service task (eg. starting a process instance through the RuntimeService, if the callActivity doesn't suit your needs). The org.activiti.engine.delegate.DelegateExecution allows to easily use these services through the org.activiti.engine.EngineServices interface: public class StartProcessInstanceTestDelegate implements JavaDelegate { public void execute(DelegateExecution execution) throws Exception { RuntimeService runtimeService = execution.getEngineServices().getRuntimeService(); runtimeService.startProcessInstanceByKey("myProcess"); } } All of the Activiti service API's are available through this interface. All data changes that occur as an effect of using these API calls, will be part of the current transaction. This also works in environments with dependency injection like Spring and CDI with or without a JTA enabled datasource. For example, the following snippet of code will do the same as the snippet above, but now the RuntimeService is injected rather than it is being fetched through the org.activiti.engine.EngineServices interface. @Component("startProcessInstanceDelegate") public class StartProcessInstanceTestDelegateWithInjection { @Autowired private RuntimeService runtimeService; public void startProcess() { runtimeService.startProcessInstanceByKey("oneTaskProcess"); } } Important technical note: since the service call is being done as part of the current transaction any data that was produced or altered before the service task is executed, is not yet flushed to the database. All API calls work on the database data, which means that these uncommitted changes are not be 'visible' within the api call of the service task.
Web Service Task [EXPERIMENTAL]
Description A Web Service task is used to synchronously invoke an external Web service.
Graphical Notation A Web Service task is visualized the same as a Java service task.
XML representation To use a Web service we need to import its operations and complex types. This can be done automatically by using the import tag pointing to the WSDL of the Web service: <import importType="http://schemas.xmlsoap.org/wsdl/" location="http://localhost:63081/counter?wsdl" namespace="http://webservice.activiti.org/" /> The previous declaration tells Activiti to import the definitions but it doesn't create the item definitions and messages for you. Let's suppose we want to invoke a specific method called 'prettyPrint', therefore we will need to create the corresponding message and item definitions for the request and response messages: <message id="prettyPrintCountRequestMessage" itemRef="tns:prettyPrintCountRequestItem" /> <message id="prettyPrintCountResponseMessage" itemRef="tns:prettyPrintCountResponseItem" /> <itemDefinition id="prettyPrintCountRequestItem" structureRef="counter:prettyPrintCount" /> <itemDefinition id="prettyPrintCountResponseItem" structureRef="counter:prettyPrintCountResponse" /> Before declaring the service task, we have to define the BPMN interfaces and operations that actually reference the Web service ones. Basically, we define and 'interface' and the required 'operation's'. For each operation we reuse the previous defined message for in and out. For example, the following declaration defines the 'counter' interface and the 'prettyPrintCountOperation' operation: <interface name="Counter Interface" implementationRef="counter:Counter"> <operation id="prettyPrintCountOperation" name="prettyPrintCount Operation" implementationRef="counter:prettyPrintCount"> <inMessageRef>tns:prettyPrintCountRequestMessage</inMessageRef> <outMessageRef>tns:prettyPrintCountResponseMessage</outMessageRef> </operation> </interface> Then we can declare a Web Service Task by using the ##WebService implementation and a reference to the Web service operation. <serviceTask id="webService" name="Web service invocation" implementation="##WebService" operationRef="tns:prettyPrintCountOperation">
Web Service Task IO Specification Unless we are using the simplistic approach for data input and output associations (See below), each Web Service Task needs to declare an IO Specification which states which are the inputs and outputs of the task. The approach is pretty straightforward and BPMN 2.0 complaint, for our prettyPrint example we define the input and output sets according to the previously declared item definitions: <ioSpecification> <dataInput itemSubjectRef="tns:prettyPrintCountRequestItem" id="dataInputOfServiceTask" /> <dataOutput itemSubjectRef="tns:prettyPrintCountResponseItem" id="dataOutputOfServiceTask" /> <inputSet> <dataInputRefs>dataInputOfServiceTask</dataInputRefs> </inputSet> <outputSet> <dataOutputRefs>dataOutputOfServiceTask</dataOutputRefs> </outputSet> </ioSpecification>
Web Service Task data input associations There are 2 ways of specifying data input associations: Using expressions Using the simplistic approach To specify the data input association using expressions we need to define the source and target items and specify the corresponding assignments between the fields of each item. In the following example we assign prefix and suffix fields of the items: <dataInputAssociation> <sourceRef>dataInputOfProcess</sourceRef> <targetRef>dataInputOfServiceTask</targetRef> <assignment> <from>${dataInputOfProcess.prefix}</from> <to>${dataInputOfServiceTask.prefix}</to> </assignment> <assignment> <from>${dataInputOfProcess.suffix}</from> <to>${dataInputOfServiceTask.suffix}</to> </assignment> </dataInputAssociation> On the other hand we can use the simplistic approach which is much more simple. The 'sourceRef' element is an Activiti variable name and the 'targetRef' element is a property of the item definition. In the following example we assign to the 'prefix' field the value of the variable 'PrefixVariable' and to the 'suffix' field the value of the variable 'SuffixVariable'. <dataInputAssociation> <sourceRef>PrefixVariable</sourceRef> <targetRef>prefix</targetRef> </dataInputAssociation> <dataInputAssociation> <sourceRef>SuffixVariable</sourceRef> <targetRef>suffix</targetRef> </dataInputAssociation>
Web Service Task data output associations There are 2 ways of specifying data out associations: Using expressions Using the simplistic approach To specify the data out association using expressions we need to define the target variable and the source expression. The approach is pretty straightforward and similar data input associations: <dataOutputAssociation> <targetRef>dataOutputOfProcess</targetRef> <transformation>${dataOutputOfServiceTask.prettyPrint}</transformation> </dataOutputAssociation> On the other hand we can use the simplistic approach which is much more simple. The 'sourceRef' element is a property of the item definition and the 'targetRef' element is an Activiti variable name. The approach is pretty straightforward and similar data input associations: <dataOutputAssociation> <sourceRef>prettyPrint</sourceRef> <targetRef>OutputVariable</targetRef> </dataOutputAssociation>
Business Rule Task [EXPERIMENTAL]
Description A Business Rule task is used to synchronously execute one or more rules. Activiti uses Drools Expert, the Drools rule engine to execute business rules. Currently, the .drl files containing the business rules have to be deployed together with the process definition that defines a business rule task to execute those rules. This means that all .drl files that are used in a process have to be packaged in the process BAR file like for example the task forms. For more information about creating business rules for Drools Expert please refer to the Drools documentation at JBoss Drools if you want to plug in your implementation of the rule task, e.g. because you want to use Drools differently or you want to use a completely different rule engine, then you can use the class or expression attribute on the BusinessRuleTask and it will behave exactly like a ServiceTask
Graphical Notation A Business Rule task is visualized the with a table icon.
XML representation To execute one or more business rules that are deployed in the same BAR file as the process definition, we need to define the input and result variables. For the input variable definition a list of process variables can be defined separated by a comma. The output variable definition can only contain one variable name that will be used to store the output objects of the executed business rules in a process variable. Note that the result variable will contain a List of objects. If no result variable name is specified by default org.activiti.engine.rules.OUTPUT is used. The following business rule task executes all business rules deployed with the process definition: <process id="simpleBusinessRuleProcess"> <startEvent id="theStart" /> <sequenceFlow sourceRef="theStart" targetRef="businessRuleTask" /> <businessRuleTask id="businessRuleTask" activiti:ruleVariablesInput="${order}" activiti:resultVariable="rulesOutput" /> <sequenceFlow sourceRef="businessRuleTask" targetRef="theEnd" /> <endEvent id="theEnd" /> </process> The business rule task can also be configured to execute only a defined set of rules from the deployed .drl files. A list of rule names separated by a comma must be specified for this. <businessRuleTask id="businessRuleTask" activiti:ruleVariablesInput="${order}" activiti:rules="rule1, rule2" /> In this case only rule1 and rule2 are executed. You can also define a list of rules that should be excluded from execution. <businessRuleTask id="businessRuleTask" activiti:ruleVariablesInput="${order}" activiti:rules="rule1, rule2" exclude="true" /> In this case all rules deployed in the same BAR file as the process definition will be executed, except for rule1 and rule2. As mentioned earlier another option is to hook in the implementation of the BusinessRuleTask yourself: <businessRuleTask id="businessRuleTask" activiti:class="${MyRuleServiceDelegate}" /> Now the BusinessRuleTask behaves exactly like a ServiceTask, but still keeps the BusinessRuleTask icon to visualize that we do business rule processing here.
Email Task Activiti allows to enhance business processes with automatic mail service tasks that send e-mails to one or more recipients, including support for cc, bcc, HTML content, ... etc. Note that the mail task is not an 'official' task of the BPMN 2.0 spec (and it does not have a dedicated icon as a consequence). Hence, in Activiti the mail task is implemented as a dedicated service task.
Mail server configuration The Activiti engine sends e-mails trough an external mail server with SMTP capabilities. To actually send e-mails, the engine needs to know how to reach the mail server. Following properties can be set in the activiti.cfg.xml configuration file: Mail server configuration Property Required? Description mailServerHost no The hostname of your mail server (e.g. mail.mycorp.com). Default is localhost mailServerPort yes, if not on the default port The port for SMTP traffic on the mail server. The default is 25 mailServerDefaultFrom no The default e-mail address of the sender of e-mails, when none is provided by the user. By default this is activiti@activiti.org mailServerUsername if applicable for your server Some mail servers require credentials for sending e-mail. By default not set. mailServerPassword if applicable for your server Some mail servers require credentials for sending e-mail. By default not set. mailServerUseSSL if applicable for your server Some mail servers require ssl communication. By default set to false. mailServerUseTLS if applicable for your server Some mail servers (for instance gmail) require TLS communication. By default set to false.
Defining an Email Task The Email task is implemented as a dedicated Service Task and is defined by setting 'mail' for the type of the service task. <serviceTask id="sendMail" activiti:type="mail"> The Email task is configured by field injection. All the values for these properties can contain EL expression, which are resolved at runtime during process execution. Following properties can be set: Mail server configuration Property Required? Description to yes The recipients if the e-mail. Multiple recipients are defined in a comma-separated list from no The sender e-mail address. If not provided, the default configured from address is used. subject no The subject of the e-mail. cc no The cc's of the e-mail. Multiple recipients are defined in a comma-separated list bcc no The bcc's of the e-mail. Multiple recipients are defined in a comma-separated list charset no Allows to change the charset of the email, which is necessary for many non-English languages. html no A piece of HTML that is the content of the e-mail. text no The content of the e-mail, in case one needs to send plain none-rich e-mails. Can be used in combination with html, for e-mail clients that don't support rich content. The client will then fall back to this text-only alternative. htmlVar no The name of a process variable that holds the HTML that is the content of the e-mail. The key difference between this and html is that this content will have expressions replaced before being sent by the mail task. textVar no The name of a process variable that holds the plain text content of the e-mail. The key difference between this and html is that this content will have expressions replaced before being sent by the mail task.
Example usage The following XML snippet shows an example of using the Email Task. <serviceTask id="sendMail" activiti:type="mail"> <extensionElements> <activiti:field name="from" stringValue="order-shipping@thecompany.com" /> <activiti:field name="to" expression="${recipient}" /> <activiti:field name="subject" expression="Your order ${orderId} has been shipped" /> <activiti:field name="html"> <activiti:expression> <![CDATA[ <html> <body> Hello ${male ? 'Mr.' : 'Mrs.' } ${recipientName},<br/><br/> As of ${now}, your order has been <b>processed and shipped</b>.<br/><br/> Kind regards,<br/> TheCompany. </body> </html> ]]> </activiti:expression> </activiti:field> </extensionElements> </serviceTask> with the following result:
Mule Task The mule task allows to send messages to Mule enhancing the integration features of Activiti. Note that the mule task is not an 'official' task of the BPMN 2.0 spec (and it does not have a dedicated icon as a consequence). Hence, in Activiti the mule task is implemented as a dedicated service task.
Defining an Mule Task The Mule task is implemented as a dedicated Service Task and is defined by setting 'mule' for the type of the service task. <serviceTask id="sendMule" activiti:type="mule"> The Mule task is configured by field injection. All the values for these properties can contain EL expression, which are resolved at runtime during process execution. Following properties can be set: Mule server configuration Property Required? Description endpointUrl yes The Mule endpoint you want to invoke. language yes The language you want to use to evaluate the payloadExpression field. payloadExpression yes An expression that will be the message's payload. resultVariable no The name of the variable which will store the result of the invocation.
Example usage The following XML snippet shows an example of using the Mule Task. <extensionElements> <activiti:field name="endpointUrl"> <activiti:string>vm://in</activiti:string> </activiti:field> <activiti:field name="language"> <activiti:string>juel</activiti:string> </activiti:field> <activiti:field name="payloadExpression"> <activiti:string>"hi"</activiti:string> </activiti:field> <activiti:field name="resultVariable"> <activiti:string>theVariable</activiti:string> </activiti:field> </extensionElements>
Camel Task The Camel task allows to send messages to and receive messages from Camel and thereby enhances the integration features of Activiti. Note that the Camel task is not an 'official' task of the BPMN 2.0 spec (and it does not have a dedicated icon as a consequence). Hence, in Activiti the Camel task is implemented as a dedicated service task. Also note to include the Activiti Camel module in your project to use the Camel task functionality.
Defining a Camel Task The Camel task is implemented as a dedicated Service Task and is defined by setting 'camel' for the type of the service task. <serviceTask id="sendCamel" activiti:type="camel"> The process definition itself needs nothing else then the camel type definition on a service task. The integration logic is all delegated to the Camel container. By default the Activiti Engine looks for a camelContext bean in the Spring container. The camelContext bean defines the Camel routes that will be loaded by the Camel container. In the following example the routes are loaded from a specific Java package, but you can also define routes directly in the Spring configuration itself. <camelContext id="camelContext" xmlns="http://camel.apache.org/schema/spring"> <packageScan> <package>org.activiti.camel.route</package> </packageScan> </camelContext> For more documentation about Camel routes you can look on the Camel website. The basic concepts are demonstrated through a few small samples here in this document. In the first sample, we will do the simplest form of Camel call from an activiti workflow. Let's call it SimpleCamelCall. If you want to define multiple Camel context beans and/or want to use a different bean name, this can be overridden on the Camel task definition like this: <serviceTask id="serviceTask1" activiti:type="camel"> <extensionElements> <activiti:field name="camelContext" stringValue="customCamelContext" /> </extensionElements> </serviceTask>
Simple Camel Call example All the files related to this example can be found in org.activiti.camel.examples.simpleCamelCall package of activiti-camel module. The target is simply activating a specific camel route. First of all we need an Spring context which contains the introduction to the routes as mentioned previously. This part of the file serves this purpose: <camelContext id="camelContext" xmlns="http://camel.apache.org/schema/spring"> <packageScan> <package>org.activiti.camel.examples.simpleCamelCall</package> </packageScan> </camelContext> The route itself is configured in a file named SimpleCamelCallRoute located in the directory mentioned in PackageScan tag above. Here is the route: public class SimpleCamelCallRoute extends RouteBuilder { @Override public void configure() throws Exception { from("activiti:SimpleCamelCallProcess:simpleCall").to("log:org.activiti.camel.examples.SimpleCamelCall"); } } The route just logs the message body and nothing more. Notice the format of the from endpoint. It is consisted of three parts: Endpoint URL parts: Endpoint Url Part Description activiti refers to Activiti endpoint SimpleCamelCallProcess name of the process simpleCall name of the Camel service in the process
Ok, our route is now properly configured and accessible to the Camel. Now comes the workflow part. The workflow looks like: <process id="SimpleCamelCallProcess"> <startEvent id="start"/> <sequenceFlow id="flow1" sourceRef="start" targetRef="simpleCall"/> <serviceTask id="simpleCall" activiti:type="camel"/> <sequenceFlow id="flow2" sourceRef="simpleCall" targetRef="end"/> <endEvent id="end"/> </process> In the service task, it is just mentioned that type of service is Camel and the target route should be named as simpleCall. This matches what was mentioned in the Activiti endpoint. By instantiating the process we will see an empty log entry. Great we are done for the simplest case.
Ping Pong example Our example worked but nothing is really transferred between Camel and Activiti and there is not much merit in it. In this example we try to send and receive data to and from Camel. We send a string, camel concatenates something to it and returns back the result. The sender part is trivial, we send our message in form of a variable to Camel Task. Here is our caller code: @Deployment public void testPingPong() { Map<String, Object> variables = new HashMap<String, Object>(); variables.put("input", "Hello"); Map<String, String> outputMap = new HashMap<String, String>(); variables.put("outputMap", outputMap); runtimeService.startProcessInstanceByKey("PingPongProcess", variables); assertEquals(1, outputMap.size()); assertNotNull(outputMap.get("outputValue")); assertEquals("Hello World", outputMap.get("outputValue")); } variable "input" is actually the input for the Camel route and outputMap is there to capture the result back from Camel. The process should be something like this: <process id="PingPongProcess"> <startEvent id="start"/> <sequenceFlow id="flow1" sourceRef="start" targetRef="ping"/> <serviceTask id="ping" activiti:type="camel"/> <sequenceFlow id="flow2" sourceRef="ping" targetRef="saveOutput"/> <serviceTask id="saveOutput" activiti:class="org.activiti.camel.examples.pingPong.SaveOutput" /> <sequenceFlow id="flow3" sourceRef="saveOutput" targetRef="end"/> <endEvent id="end"/> </process> Note that SaveOutput Service task, stores the value of "Output" variable from context to the previously mentioned OutputMap. Now we have to know how the variables are send to Camel and returned back. Here comes the notion of Camel behaviour into the play. The way variables are communicated to Camel is configurable via CamelBehavior. Here we use Default one in our sample, a short description of the other ones comes afterwards. With such a code you can configure the desired camel behaviour: <serviceTask id="serviceTask1" activiti:type="camel"> <extensionElements> <activiti:field name="camelBehaviorClass" stringValue="org.activiti.camel.impl.CamelBehaviorCamelBodyImpl" /> </extensionElements> </serviceTask> If you do not specify and specific behaviour then, org.activiti.camel.impl.CamelBehaviorDefaultImpl will be set. This behaviour copies the variables to Camel properties of the same name. In return regardless of selected behaviour, if the camel message body is a map, then each of its elements is copied as a variable, else the whole object is copied into a specific variable with the name of "camelBody". Knowing that, this camel route concludes our second example: @Override public void configure() throws Exception { from("activiti:PingPongProcess:ping").transform().simple("${property.input} World"); } In this route, the string "world" is concatenated to the end of property named "input" and the result will be in the message body. It is accessible by checking "camelBody" variable in the java service task and copied to "outputMap" and checked in test case. Now that the example on its default behaviour works, lets see what are the other possibilities. In starting every camel route, the Process Instance ID will be copied into a camel property with the specific name of "PROCESS_ID_PROPERTY". It is later used for correlating the process instance and camel route. Also it can be exploited in the Camel route. There are three different behaviours already available out of the box in Activiti. The behaviour can be overwritten by a specific phrase in the route URL. Here is an example of overriding the already defined behaviour in URL: from("activiti:asyncCamelProcess:serviceTaskAsync2?copyVariablesToProperties=true"). the following table provides an overview of three available camel behaviours: Existing camel behaviours: Behaviour In Url Description CamelBehaviorDefaultImpl copyVariablesToProperties Copy Activiti variables as Camel properties CamelBehaviorCamelBodyImpl copyCamelBodyToBody Copy only Activiti variable named "camelBody" as camel message body CamelBehaviorBodyAsMapImpl copyVariablesToBodyAsMap Copy all the activiti variables in a map as Camel message body
the above table explains how activiti variables are going to be transfered to Camel. The following table explains how the Camel variables are returned back to Activiti. This can only be configured in route URLs. Existing camel behaviours: Url Description Default If Camel body is a map, copy each element as Activiti variable, otherwise copy the whole Camel body as "camelBody" activiti variable copyVariablesFromProperties Copy Camel properties as Activiti variables of the same name copyCamelBodyToBodyAsString like default, but if camel Body is not a map, first convert it to String and then copy it in "camelBody" isCopyVariablesFromHeader Additionally copy camel headers to Activiti variables of the same names
Source files of this example are available in org.activiti.camel.examples.pingPong package of activiti-camel module as well.
Asynchronous Ping Pong example Previous examples were all synchronous. The workflow stops, until the camel route is concluded and returned. In some cases, we might need the activiti workflow to continue. For such purposes the asynchronous capability of the Camel service task is useful. You can make use of this feature by setting the async property of the Camel service task to true. <serviceTask id="serviceAsyncPing" activiti:type="camel" activiti:async="true"/> By setting this feature the specified Camel route is activated asynchronously by the Activiti job executor. When you define a queue in the Camel route the Activiti process will continue with the activities after the Camel service task. The Camel route will be executed fully asynchronously from the process execution. If you want to wait for a response of the Camel service task somewhere in your process definition, you can use a receive task. <receiveTask id="receiveAsyncPing" name="Wait State" /> The process instance will wait until a signal is received, for example from Camel. In Camel you can send a signal to the process instance by sending a message to the proper activiti endpoint. from("activiti:asyncPingProcess:serviceAsyncPing").to("activiti:asyncPingProcess:receiveAsyncPing"); As usual endpoint consists of three parts separated by colons: constant string "activiti" process name receive task name
Instantiate workflow from Camel route In our all previous examples Activiti workflow started first and the Camel route was started within workflow. It is also possible from the other side. It is possible that a workflow is instantiated from an already started camel route. It is very similar to signalling receive task, except that the last part is not there. Here is a sample route: from("direct:start").to("activiti:camelProcess"); as you see the url has two parts, the first is constant string "activiti" and the second name is the name of the process. Obviously the process should already be deployed and startable by engine configuration.
Manual Task
Description A Manual Task defines a task that is external to the BPM engine. It is used to model work that is done by somebody, which the engine does not need to know of, nor is there a system or UI interface. For the engine, a manual task is handled as a pass-through activity, automatically continuing the process from the moment process execution arrives into it.
Graphical Notation A manual task is visualized as a rounded rectangle, with a little 'hand' icon in the upper left corner
XML representation <manualTask id="myManualTask" name="Call client for more information" />
Java Receive Task
Description A Receive Task is a simple task that waits for the arrival of a certain message. Currently, we have only implemented Java semantics for this task. When process execution arrives at a Receive Task, the process state is committed to the persistence store. This means that the process will stay in this wait state, until a specific message is received by the engine, which triggers the continuation of the process past the Receive Task.
Graphical notation A Receive Task is visualized as a task (rounded rectangle) with a message icon in the top left corner. The message is white (a black message icon would have send semantics)
XML representation <receiveTask id="waitState" name="wait" /> To continue a process instance that is currently waiting at such a Receive Task, the runtimeService.signal(executionId) must be called using the id of the execution that arrived in the Receive Task. The following code snippet shows how this works in practice: ProcessInstance pi = runtimeService.startProcessInstanceByKey("receiveTask"); Execution execution = runtimeService.createExecutionQuery() .processInstanceId(pi.getId()) .activityId("waitState") .singleResult(); assertNotNull(execution); runtimeService.signal(execution.getId());
Shell Task
Description The shell task allows to run shell scripts and commands. Note that the Shell task is not an 'official' task of BPMN 2.0 spec (and it does not have a dedicated icon as a consequence).
defining a shell task The shell task is implemented as a dedicated Service Task and is defined by setting 'shell' for the type of the service task. <serviceTask id="shellEcho" activiti:type="shell"> The Shell task is configured by field injection. All the values for these properties can contain EL expression, which are resolved at runtime during process execution. Following properties could be set: Shell task parameter configuration Property Required? Type Description Default command yes String Shell command to execute. arg0-5 no String Parameter 0 to Parameter 5 wait no true/false wait if necessary, until the shell process has terminated. true redirectError no true/false Merge standard error with the standard output. false cleanEnv no true/false Shell process does not inherit current environment. false outputVariable no String Name of variable to contain the output Output is not recorded. errorCodeVariable no String Name of variable to contain result error code Error level is not registered. directory no String Default directory of shell process Current directory
Example usage The following XML snippet shows an example of using the shell Task. It runs shell script "cmd /c echo EchoTest", waits for it to be terminated and puts the result in resultVar <serviceTask id="shellEcho" activiti:type="shell" > <extensionElements> <activiti:field name="command" stringValue="cmd" /> <activiti:field name="arg1" stringValue="/c" /> <activiti:field name="arg2" stringValue="echo" /> <activiti:field name="arg3" stringValue="EchoTest" /> <activiti:field name="wait" stringValue="true" /> <activiti:field name="outputVariable" stringValue="resultVar" /> </extensionElements> </serviceTask>
Execution listener Compatibility note: After releasing 5.3, we discovered that execution listeners and task listeners and expressions were still in non-public API. Those classes were in subpackages of org.activiti.engine.impl..., which has impl in it). org.activiti.engine.impl.pvm.delegate.ExecutionListener, org.activiti.engine.impl.pvm.delegate.TaskListener and org.activiti.engine.impl.pvm.el.Expression have been deprecated. From now on, you should use org.activiti.engine.delegate.ExecutionListener, org.activiti.engine.delegate.TaskListener and org.activiti.engine.delegate.Expression. In the new publicly available API, access to ExecutionListenerExecution.getEventSource() has been removed. Apart from the deprecation compiler warning, the existing code should run fine. But consider switching to the new public API interfaces (without .impl. in the package name). Execution listeners allow you to execute external Java code or evaluate an expression when certain events occur during process execution. The events that can be captured are: Start and ending of a process instance. Taking a transition. Start and ending of an activity. Start and ending of a gateway. Start and ending of intermediate events. Ending an start event or starting an end event. The following process definition contains 3 execution listeners: <process id="executionListenersProcess"> <extensionElements> <activiti:executionListener class="org.activiti.examples.bpmn.executionlistener.ExampleExecutionListenerOne" event="start" /> </extensionElements> <startEvent id="theStart" /> <sequenceFlow sourceRef="theStart" targetRef="firstTask" /> <userTask id="firstTask" /> <sequenceFlow sourceRef="firstTask" targetRef="secondTask"> <extensionElements> <activiti:executionListener class="org.activiti.examples.bpmn.executionListener.ExampleExecutionListenerTwo" /> </extensionElements> </sequenceFlow> <userTask id="secondTask" > <extensionElements> <activiti:executionListener expression="${myPojo.myMethod(execution.event)}" event="end" /> </extensionElements> </userTask> <sequenceFlow sourceRef="secondTask" targetRef="thirdTask" /> <userTask id="thirdTask" /> <sequenceFlow sourceRef="thirdTask" targetRef="theEnd" /> <endEvent id="theEnd" /> </process> The first execution listener is notified when the process starts. The listener is an external Java-class (like ExampleExecutionListenerOne) and should implement org.activiti.engine.delegate.ExecutionListener interface. When the event occurs (in this case end event) the method notify(ExecutionListenerExecution execution) is called. public class ExampleExecutionListenerOne implements ExecutionListener { public void notify(ExecutionListenerExecution execution) throws Exception { execution.setVariable("variableSetInExecutionListener", "firstValue"); execution.setVariable("eventReceived", execution.getEventName()); } } It is also possible to use a delegation class that implements the org.activiti.engine.delegate.JavaDelegate interface. These delegation classes can then be reused in other constructs, such as a delegation for a serviceTask. The second execution listener is called when the transition is taken. Note that the listener element doesn't define an event, since only take events are fired on transitions. Values in the event attribute are ignored when a listener is defined on a transition. The last execution listener is called when activity secondTask ends. Instead of using the class on the listener declaration, a expression is defined instead which is evaluated/invoked when the event is fired. <activiti:executionListener expression="${myPojo.myMethod(execution.eventName)}" event="end" /> As with other expressions, execution variables are resolved and can be used. Because the execution implementation object has a property that exposes the event name, it's possible to pass the event-name to your methods using execution.eventName. Execution listeners also support using a delegateExpression, similar to a service task. <activiti:executionListener event="start" delegateExpression="${myExecutionListenerBean}" /> In Activiti 5.12 we also introduced a new type of execution listener, the org.activiti.engine.impl.bpmn.listener.ScriptExecutionListener. This script execution listener allows you to execute a piece of script logic for an execution listener event. <activiti:executionListener event="start" class="org.activiti.engine.impl.bpmn.listener.ScriptExecutionListener" > <activiti:field name="script"> <activiti:string> def bar = "BAR"; // local variable foo = "FOO"; // pushes variable to execution context execution.setVariable("var1", "test"); // test access to execution instance bar // implicit return value </activiti:string> </activiti:field> <activiti:field name="language" stringValue="groovy" /> <activiti:field name="resultVariable" stringValue="myVar" /> <activiti:executionListener>
Field injection on execution listeners When using an execution listener that is configured with the class attribute, field injection can be applied. This is exactly the same mechanism as used Service task field injection, which contains an overview of the possibilities provided by field injection. The fragment below shows a simple example process with an execution listener with fields injected. <process id="executionListenersProcess"> <extensionElements> <activiti:executionListener class="org.activiti.examples.bpmn.executionListener.ExampleFieldInjectedExecutionListener" event="start"> <activiti:field name="fixedValue" stringValue="Yes, I am " /> <activiti:field name="dynamicValue" expression="${myVar}" /> </activiti:executionListener> </extensionElements> <startEvent id="theStart" /> <sequenceFlow sourceRef="theStart" targetRef="firstTask" /> <userTask id="firstTask" /> <sequenceFlow sourceRef="firstTask" targetRef="theEnd" /> <endEvent id="theEnd" /> </process> public class ExampleFieldInjectedExecutionListener implements ExecutionListener { private Expression fixedValue; private Expression dynamicValue; public void notify(ExecutionListenerExecution execution) throws Exception { execution.setVariable("var", fixedValue.getValue(execution).toString() + dynamicValue.getValue(execution).toString()); } } The class ExampleFieldInjectedExecutionListener concatenates the 2 injected fields (one fixed an the other dynamic) and stores this in the process variable 'var'. @Deployment(resources = {"org/activiti/examples/bpmn/executionListener/ExecutionListenersFieldInjectionProcess.bpmn20.xml"}) public void testExecutionListenerFieldInjection() { Map<String, Object> variables = new HashMap<String, Object>(); variables.put("myVar", "listening!"); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("executionListenersProcess", variables); Object varSetByListener = runtimeService.getVariable(processInstance.getId(), "var"); assertNotNull(varSetByListener); assertTrue(varSetByListener instanceof String); // Result is a concatenation of fixed injected field and injected expression assertEquals("Yes, I am listening!", varSetByListener); }
Task listener A task listener is used to execute custom Java logic or an expression upon the occurrence of a certain task-related event. A task listener can only be added in the process definition as a child element of a user task. Note that this also must happen as a child of the BPMN 2.0 extensionElements and in the activiti namespace, since a task listener is an Activiti-specific construct. <userTask id="myTask" name="My Task" > <extensionElements> <activiti:taskListener event="create" class="org.activiti.MyTaskCreateListener" /> </extensionElements> </userTask> A task listener supports following attributes: event (required): the type of task event on which the task listener will be invoked. Possible events are create: occurs when the task has been created an all task properties are set. assignment: occurs when the task is assigned to somebody. Note: when process execution arrives in a userTask, first an assignment event will be fired, before the create event is fired. This might seem an unnatural order, but the reason is pragmatic: when receiving the create event, we usually want to inspect all properties of the task including the assignee. complete: occurs when the task is completed and just before the task is deleted from the runtime data. delete: occurs just before the task is going to be deleted. Notice that it will also be executed when task is normally finished via completeTask. class: the delegation class that must be called. This class must implement the org.activiti.engine.impl.pvm.delegate.TaskListener interface. public class MyTaskCreateListener implements TaskListener { public void notify(DelegateTask delegateTask) { // Custom logic goes here } } It is also possible to use field injection to pass process variables or the execution to the delegation class. Note that an instance of the delegation class is created upon process deployment (as is the case with any class delegation in Activiti), which means that the instance is shared between all process instance executions. expression: (cannot be used together with the class attribute): specifies an expression that will be executed when the event happens. It is possible to pass the DelegateTask object and the name of the event (using task.eventName) as parameter to the called object. <activiti:taskListener event="create" expression="${myObject.callMethod(task, task.eventName)}" /> delegateExpression allows to specify an expression that resolves to an object implementing the TaskListener interface, similar to a service task. <activiti:taskListener event="create" delegateExpression="${myTaskListenerBean}" /> In Activiti 5.12 we also introduced a new type of task listener, the org.activiti.engine.impl.bpmn.listener.ScriptTaskListener. This script task listener allows you to execute a piece of script logic for an task listener event. <activiti:taskListener event="complete" class="org.activiti.engine.impl.bpmn.listener.ScriptTaskListener" > <activiti:field name="script"> <activiti:string> def bar = "BAR"; // local variable foo = "FOO"; // pushes variable to execution context task.setOwner("kermit"); // test access to task instance bar // implicit return value </activiti:string> </activiti:field> <activiti:field name="language" stringValue="groovy" /> <activiti:field name="resultVariable" stringValue="myVar" /> <activiti:taskListener>
Multi-instance (for each)
Description A multi-instance activity is a way of defining repetition for a certain step in a business process. In programming concepts, a multi-instance matches the for each construct: it allows to execute a certain step or even a complete subprocess for each item in a given collection, sequentially or in parallel. A multi-instance is a regular activity that has extra properties defined (so-called 'multi-instance characteristics'') which will cause the activity to be executed multiple times at runtime. Following activities can become a multi-instance activity: User Task Script Task Java Service Task Web Service Task Business Rule Task Email Task Manual Task Receive Task (Embedded) Sub-Process Call Activity A Gateway or Event can not become multi-instance. As required by the spec, each parent execution of the created executions for each instance will have following variables: nrOfInstances: the total number of instances nrOfActiveInstances: the number of currently active, i.e. not yet finished, instances. For a sequential multi-instance, this will always be 1. nrOfCompletedInstances: the number of already completed instances. These values can be retrieved by calling the execution.getVariable(x) method. Additionally, each of the created executions will have an execution-local variable (i.e. not visible for the other executions, and not stored on process instance level) : loopCounter: indicates the index in the for-each loop of that particular instance. loopCounter variable can be renamed by activiti elementIndexVariable attribute.
Graphical notation If an activity is multi-instance, this is indicated by three short lines at the bottom of that activity. Three vertical lines indicates that the instances will be executed in parallel, while three horizontal lines indicate sequential execution.
Xml representation To make an activity multi-instance, the activity xml element must have a multiInstanceLoopCharacteristics child element. <multiInstanceLoopCharacteristics isSequential="false|true"> ... </multiInstanceLoopCharacteristics> The isSequential attribute indicates if the instances of that activity are executed sequentially or parallel. The number of instances are calculated once, when entering the activity. There are a few ways of configuring this. On way is directly specifying a number, by using the loopCardinality child element. <multiInstanceLoopCharacteristics isSequential="false|true"> <loopCardinality>5</loopCardinality> </multiInstanceLoopCharacteristics> Expressions that resolve to a positive number are also possible: <multiInstanceLoopCharacteristics isSequential="false|true"> <loopCardinality>${nrOfOrders-nrOfCancellations}</loopCardinality> </multiInstanceLoopCharacteristics> Another way to define the number of instances, is to specify the name of a process variable which is a collection using the loopDataInputRef child element. For each item in the collection, an instance will be created. Optionally, it is possible to set that specific item of the collection for the instance using the inputDataItem child element. This is shown in the following XML example: <userTask id="miTasks" name="My Task ${loopCounter}" activiti:assignee="${assignee}"> <multiInstanceLoopCharacteristics isSequential="false"> <loopDataInputRef>assigneeList</loopDataInputRef> <inputDataItem name="assignee" /> </multiInstanceLoopCharacteristics> </userTask> Suppose the variable assigneeList contains the values [kermit, gonzo, fozzie]. In the snippet above, three user tasks will be created in parallel. Each of the executions will have a process variable named assignee containing one value of the collection, which is used to assign the user task in this example. The downside of the loopDataInputRef and inputDataItem is that 1) the names are pretty hard to remember and 2) due to the BPMN 2.0 schema restrictions they can't contain expressions. Activiti solves this by offering the collection and elementVariable attributes on the multiInstanceCharacteristics: <userTask id="miTasks" name="My Task" activiti:assignee="${assignee}"> <multiInstanceLoopCharacteristics isSequential="true" activiti:collection="${myService.resolveUsersForTask()}" activiti:elementVariable="assignee" > </multiInstanceLoopCharacteristics> </userTask> A multi-instance activity ends when all instances are finished. However, it is possible to specify an expression that is evaluated every time one instance ends. When this expression evaluates to true, all remaining instances are destroyed and the multi-instance activity ends, continuing the process. Such an expression must be defined in the completionCondition child element. <userTask id="miTasks" name="My Task" activiti:assignee="${assignee}"> <multiInstanceLoopCharacteristics isSequential="false" activiti:collection="assigneeList" activiti:elementVariable="assignee" > <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6 }</completionCondition> </multiInstanceLoopCharacteristics> </userTask> In this example, there will be parallel instances created for each element of the assigneeList collection. However, when 60% of the tasks are completed, the other tasks are deleted and the process continues.
Boundary events and multi-instance Since a multi-instance is a regular activity, it is possible to define a boundary event on its boundary. In case of an interrupting boundary event, when the event is caught, all instances that are still active will be destroyed. Take for example following multi-instance subprocess: Here, all instances of the subprocess will be destroyed when the timer fires, regardless of how many instances there are or which inner activities are currently not yet completed.
Compensation Handlers
Description [EXPERIMENTAL] If an activity is used for compensating the effects of another activity, it can be declared to be a compensation handler. Compensation handlers are not contained in normal flow and are only executed when a compensation event is thrown. Compensation handlers must not have incoming or outgoing sequence flows. A compensation handler must be associated with a compensation boundary event using a directed association.
Graphical notation If an activity is a compensation handler, the compensation event icon is displayed in the center bottom area. The following excerpt from a process diagram shows a service task with an attached compensation boundary event which is associated to a compensation handler. Notice the compensation handler icon in the bottom canter area of the "cancel hotel reservation" service task
XML representation In order to declare an activity to be a compensation handler, we need to set the attribute isForCompensation to true: <serviceTask id="undoBookHotel" isForCompensation="true" activiti:class="..."> </serviceTask>
Sub-Processes and Call Activities
Sub-Process
Description A Sub-Process is an activity that contains other activities, gateways, events, etc. which on itself form a process that is part of the bigger process. A Sub-Process is completely defined inside a parent process (that's why it's often called an embedded Sub-Process). Sub-Processes have two major use cases: Sub-Processes allow hierarchical modeling. Many modeling tools allow that Sub-Processes can be collapsed, hiding all the details of the Sub-Process and displaying a high-level end-to-end overview of the business process. A Sub-Process creates a new scope for events. Events that are thrown during execution of the Sub-Process, can be caught by a boundary event on the boundary of the Sub-Process, thus creating a scope for that event limited to the Sub-Process. Using a Sub-Process does impose some constraints: A Sub-Process can only have one none start event, no other start event types are allowed. A Sub-Process must at least have one end event. Note that the BPMN 2.0 specification allows to omit the start and end events in a Sub-Process, but the current Activiti implementation does not support this. Sequence flow can not cross Sub-Process boundaries.
Graphical Notation A Sub-Process is visualized as a typical activity, i.e. a rounded rectangle. In case the Sub-Process is collapsed, only the name and a plus-sign are displayed, giving a high-level overview of the process: In case the Sub-Process is expanded, the steps of the Sub-Process are displayed within the Sub-Process boundaries: One of the main reasons to use a Sub-Process, is to define a scope for a certain event. The following process model shows this: both the investigate software/investigate hardware tasks need to be done in parallel, but both tasks need to be done within a certain time, before Level 2 support is consulted. Here, the scope of the timer (i.e. which activities must be done in time) is constrained by the Sub-Process.
XML representation A Sub-Process is defined by the subprocess element. All activities, gateways, events, etc. that are part of the Sub-Process, need to be enclosed within this element. <subProcess id="subProcess"> <startEvent id="subProcessStart" /> ... other Sub-Process elements ... <endEvent id="subProcessEnd" /> </subProcess>
Event Sub-Process
Description The Event Sub-Process is new in BPMN 2.0. An Event Sub-Process is a subprocess that is triggered by an event. An Event Sub-Process can be added at the process level or at any subprocess level. The event used to trigger an event subprocess is configured using a start event. From this, it follows that none start events are not supported for Event Sub-Processes. An Event Sub-Process might be triggered using events like message events, error events, signal events, timer events, or compensation events. The subscription to the start event is created when the scope (process instance or subprocess) hosting the Event Sub-Process is created. The subscription is removed when the scope is destroyed. An Event Sub-Process may be interrupting or non-interrupting. An interrupting subprocess cancels any executions in the current scope. A non-interrupting Event Sub-Process spawns a new concurrent execution. While an interrupting Event Sub-Process can only be triggered once for each activation of the scope hosting it, a non-interrupting Event Sub-Process can be triggered multiple times. The fact whether the subprocess is interrupting is configured using the start event triggering the Event Sub-Process. An Event Sub-Process must not have any incoming or outgoing sequence flows. Since an Event Sub-Process is triggered by an event, an incoming sequence flow makes no sense. When an Event Sub-Process is ended, either the current scope is ended (in case of an interrupting Event Sub-Process), or the concurrent execution spawned for the non-interrupting subprocess is ended. Current limitations: Activiti only supports interrupting Event Sub-Processes. Activiti only supports Event Sub-Process triggered using an Error Start Event or Message Start Event.
Graphical Notation An Event Sub-Process might be visualized as a an embedded subprocess with a dotted outline.
XML representation An Event Sub-Process is represented using XML in the same way as a an embedded subprocess. In addition the attribute triggeredByEvent must have the value true: <subProcess id="eventSubProcess" triggeredByEvent="true"> ... </subProcess>
Example The following is an example of an Event Sub-Process triggered using an Error Start Event. The Event Sub-Process is located at the "process level", i.e. is scoped to the process instance: This is how the Event Sub-Process would look like in XML: <subProcess id="eventSubProcess" triggeredByEvent="true"> <startEvent id="catchError"> <errorEventDefinition errorRef="error" /> </startEvent> <sequenceFlow id="flow2" sourceRef="catchError" targetRef="taskAfterErrorCatch" /> <userTask id="taskAfterErrorCatch" name="Provide additional data" /> </subProcess> As already stated, an Event Sub-Process can also be added to an embedded subprocess. If it is added to an embedded subprocess, it becomes an alternative to a boundary event. Consider the two following process diagrams. In both cases the embedded subprocess throws an error event. Both times the error is caught and handled using a user task. as opposed to: In both cases the same tasks are executed. However, there are differences between both modelling alternatives: The embedded subprocess is executed using the same execution which executed the scope it is hosted in. This means that an embedded subprocess has access to the variables local to it's scope. When using a boundary event, the execution created for executing the embedded subprocess is deleted by the sequence flow leaving the boundary event. This means that the variables created by the embedded subprocess are not available anymore. When using an Event Sub-Process, the event is completely handled by the subprocess it is added to. When using a boundary event, the event is handled by the parent process. These two differences can help you decide whether a boundary event or an embedded subprocess is better suited for solving a particular process modeling / implementation problem.
Transaction subprocess [EXPERIMENTAL]
Description A transaction subprocess is an embedded subprocess, which can be used to group multiple activities to a transaction. A transaction is a logical unit of work which allows to group a set of individual activities, such that they either succeed or fail collectively. Possible outcomes of a transaction: A transaction can have three different outcomes: A transaction is successful, if it is neither cancelled not terminated by a hazard. If a transaction subprocess is successful, it is left using the outgoing sequenceflow(s). A successful transaction might be compensated if a compensation event is thrown later in the process. Note: just as "ordinary" embedded subprocesses, a transaction may be compensated after successful completion using an intermediary throwing compensation event. A transaction is cancelled, if an execution reaches the cancel end event. In that case, all executions are terminated and removed. A single remaining execution is then set to the cancel boundary event, which triggers compensation. After compensation is completed, the transaction subprocess is left using the outgoing sequence flow(s) of the cancel boundary event. A transaction is ended by a hazard, if an error event is thrown, that is not caught within the scope of the transaction subprocess. (This also applies if the error is caught on the boundary of the transaction subprocess.) In this case, compensation is not performed. The following diagram illustrates the three different outcomes: Relation to ACID transactions: it is important not to confuse the bpmn transaction subprocess with technical (ACID) transactions. The bpmn transaction subprocess is not a way to scope technical transactions. In order to understand transaction management in Activiti, read the section on concurrency and transactions. A bpmn transaction is different from a technical transaction in the following ways: While an ACID transaction is typically short lived, a bpmn transaction may take hours, days or even months to complete. (Consider the case where one of the activities grouped by a transaction is a usertask, typically people have longer response times than applications. Or, in another situation, a bpmn transaction might wait for some business event to occur, like the fact that a particular order has been fulfilled.) Such operations usually take considerably longer to complete than updating a record in a database, or storing a message using a transactional queue. Because it is impossible to scope a technical transaction to the duration of a business activity, a bpmn transaction typically spans multiple ACID transactions. Since a bpmn transaction spans multiple ACID transactions, we loose ACID properties. For example, consider the example given above. Let's assume the "book hotel" and the "charge credit card" operations are performed in separate ACID transactions. Let's also assume that the "book hotel" activity is successful. Now we have an intermediary inconsistent state, because we have performed an hotel booking but have not yet charged the credit card. Now, in an ACID transaction, we would also perform different operations sequentially and thus also have an intermediary inconsistent state. What is different here, is that the inconsistent state is visible outside of the scope of the transaction. For example, if the reservations are made using an external booking service, other parties using the same booking service might already see that the hotel is booked. This means, that when implementing business transactions, we completely loose the isolation property (Granted: we usually also relax isolation when working with ACID transactions to allow for higher levels of concurrency, but there we have fine grained control and intermediary inconsistencies are only present for very short periods of times). A bpmn business transaction can also not be rolled back in the traditional sense. Since it spans multiple ACID transactions, some of these ACID transactions might already be committed at the time the bpmn transaction is cancelled. At this point, they cannot be rolled back anymore. Since bpmn transactions are long-running in nature, the lack of isolation and a rollback mechanism need to be dealt with differently. In practice, there is usually no better solution than to deal with these problems in a domain specific way: The rollback is performed using compensation. If a cancel event is thrown in the scope of a transaction, the effects of all activities that executed successfully and have a compensation handler are compensated. The lack of isolation is also often dealt with using domain specific solutions. For instance, in the example above, an hotel room might appear to be booked to a second customer, before we have actually made sure that the first customer can pay for it. Since this might be undesirable from a business perspective, a booking service might choose to allow for a certain amount of overbooking. In addition, since the transaction can be aborted in case of a hazard, the booking service has to deal with the situation where a hotel room is booked but payment is never attempted (since the transaction was aborted). In that case the booking service might choose a strategy where a hotel room is reserved for a maximum period of time and if payment is not received until then, the booking is cancelled. To sum it up: while ACID transactions offer a generic solution to such problems (rollback, isolation levels and heuristic outcomes), we need to find domain specific solutions to these problems when implementing business transactions. Current limitations: The bpmn specification requires that the process engine reacts to events issued by the underlying transaction protocol and for instance that a transaction is cancelled, if a cancel event occurs in the underlying protocol. As an embeddable engine, Activiti does currently not support this. (For some ramifications of this, see paragraph on consistency below.) Consistency on top of ACID transactions and optimistic concurrency: A bpmn transaction guarantees consistency in the sense that either all activities compete successfully, or if some activity cannot be performed, the effects of all other successful activities are compensated. So either way we end up in a consistent state. However, it is important to recognize that in Activiti, the consistency model for bpmn transactions is superposed on top of the consistency model for process execution. Activiti executes processes in a transactional way. Concurrency is addressed using optimistic locking. In Activiti, bpmn error, cancel and compensation events are built on top of the same acid transactions and optimistic locking. For example, a cancel end event can only trigger compensation if it is actually reached. It is not reached if some undeclared exception is thrown by a service task before. Or, the effects of a compensation handler can not be committed if some other participant in the underlying ACID transaction sets the transaction to the state rollback-only. Or, when two concurrent executions reach a cancel end event, compensation might be triggered twice and fail with an optimistic locking exception. All of this is to say that when implementing bpmn transactions in Activiti, the same set of rules apply as when implementing "ordinary" processes and subprocesses. So to effectively guarantee consistency, it is important to implement processes in a way that does take the optimistic, transactional execution model into consideration.
Graphical Notation An transaction subprocess might be visualized as a an embedded subprocess with a double outline.
XML representation A transaction subprocess is represented using xml using the transaction tag: <transaction id="myTransaction" > ... </transaction>
Example The following is an example of a transaction subprocess:
Call activity (subprocess)
Description BPMN 2.0 makes a distinction between a regular subprocess, often also called embedded subprocess, and the call activity, which looks very similar. From a conceptual point of view, both will call a subprocess when process execution arrives at the activity. The difference is that the call activity references a process that is external to the process definition, whereas the subprocess is embedded within the original process definition. The main use case for the call activity is to have a reusable process definition that can be called from multiple other process definitions. When process execution arrives in the call activity, a new execution is created that is a sub-execution of the execution that arrives in the call activity. This sub-execution is then used to execute the subprocess, potentially creating parallel child execution as within a regular process. The super-execution waits until the subprocess is completely ended, and continues the original process afterwards.
Graphical Notation A call activity is visualized the same as a subprocess, however with a thick border (collapsed and expanded). Depending on the modeling tool, a call activity can also be expanded, but the default visualization is the collapsed subprocess representation.
XML representation A call activity is a regular activity, that requires a calledElement that references a process definition by its key. In practice, this means that the id of the process is used in the calledElement. <callActivity id="callCheckCreditProcess" name="Check credit" calledElement="checkCreditProcess" /> Note that the process definition of the subprocess is resolved at runtime. This means that the subprocess can be deployed independently from the calling process, if needed.
Passing variables You can pass process variables to the sub process and vice versa. The data is copied into the subprocess when it is started and copied back into the main process when it ends. <callActivity id="callSubProcess" calledElement="checkCreditProcess" > <extensionElements> <activiti:in source="someVariableInMainProcess" target="nameOfVariableInSubProcess" /> <activiti:out source="someVariableInSubProcss" target="nameOfVariableInMainProcess" /> </extensionElements> </callActivity> We use an Activiti Extension as a shortcut for the BPMN standard elements called dataInputAssociation and dataOutputAssociation, which only work if you declare process variables in the BPMN 2.0 standard way. It is possible to use expressions here as well: <callActivity id="callSubProcess" calledElement="checkCreditProcess" > <extensionElements> <activiti:in sourceExpression="${x+5}"" target="y" /> <activiti:out source="${y+5}" target="z" /> </extensionElements> </callActivity> So in the end z = y+5 = x+5+5
Example The following process diagram shows a simple handling of an order. Since the checking of the customer's credit could be common to many other processes, the check credit step is modeled here as a call activity. The process looks as follows: <startEvent id="theStart" /> <sequenceFlow id="flow1" sourceRef="theStart" targetRef="receiveOrder" /> <manualTask id="receiveOrder" name="Receive Order" /> <sequenceFlow id="flow2" sourceRef="receiveOrder" targetRef="callCheckCreditProcess" /> <callActivity id="callCheckCreditProcess" name="Check credit" calledElement="checkCreditProcess" /> <sequenceFlow id="flow3" sourceRef="callCheckCreditProcess" targetRef="prepareAndShipTask" /> <userTask id="prepareAndShipTask" name="Prepare and Ship" /> <sequenceFlow id="flow4" sourceRef="prepareAndShipTask" targetRef="end" /> <endEvent id="end" /> The subprocess looks as follows: There is nothing special to the process definition of the subprocess. It could as well be used without being called from another process.
Transactions and Concurrency
Asynchronous Continuations Activiti executes processes in a transactional way which can be configured to suite your needs. Lets start by looking at how Activiti scopes transactions normally. If you trigger Activiti (i.e. start a process, complete a task, signal an execution), Activiti is going to advance in the process, until it reaches wait states on each active path of execution. More concretely speaking it performs a depth-first search through the process graph and returns if it has reached wait states on every branch of execution. A wait state is a task which is performed "later" which means that Activiti persists the current execution and waits to be triggered again. The trigger can either come from an external source for example if we have a user task or a receive message task, or from Activiti itself, if we have a timer event. This is illustrated in the following picture: We see a segment of a BPMN processes with a usertask, a service task and a timer event. Completing the usertask and validating the address is part of the same unit of work, so it should succeed or fail atomically. That means that if the service task throws an exception we want to rollback the current transaction, such that the execution tracks back to the user task and the user task is still present in the database. This is also the default behavior of Activiti. In (1) an application or client thread completes the task. In that same thread Activiti is now executing the service and advances until it reaches a wait state, in this case the timer event (2). Then it returns the control to the caller (3) potentially committing the transaction (if it was started by Activiti). In some cases this is not what we want. Sometimes we need custom control over transaction boundaries in a process, in order to be able to scope logical units of work. This is where asynchronous continuations come into play. Consider the following process (fragment): This time we are completing the user task, generating an invoice and then send that invoice to the customer. This time the generation of the invoice is not part of the same unit of work so we do not want to rollback the completion of the usertask if generating an invoice fails. So what we want Activiti to do is complete the user task (1), commit the transaction and return the control to the calling application. Then we want to generate the invoice asynchronously, in a background thread. This background thread is the Activiti job executor (actually a thread pool) which periodically polls the database for jobs. So behind the scenes, when we reach the "generate invoice" task, we are creating a job "message" for Activiti to continue the process later and persisting it into the database. This job is then picked up by the job executor and executed. We are also giving the local job executor a little hint that there is a new job, to improve performance. In order to use this feature, we can use the activiti:async="true" extension. So for example, the service task would look like this: <serviceTask id="service1" name="Generate Invoice" activiti:class="my.custom.Delegate" activiti:async="true" /> activiti:async ca be specified on the following bpmn task types: task, serviceTask, scriptTask, businessRuleTask, sendTask, receiveTask, userTask, subProcess, callActivity On a userTask, receiveTask or other wait states, the async continuation allows us to execute the start execution listeners in a separate thread/transaction.
Exclusive Jobs Since Activiti 5.9, the JobExecutor makes sure that jobs from a single process instance are never executed concurrently. Why is this?
Why exclusive Jobs? Consider the following process definition: We have a parallel gateway followed by three service tasks which all perform an asynchronous continuation. As a result of this, three jobs are added to the database. Once such a job is present in the database it can be processes by the JobExecutor. The JobExecutor acquires the jobs and delegates them to a thread pool of worker threads which actually process the jobs. This means that using an asynchronous continuation, you can distribute the work to this thread pool (and in a clustered scenario even across multiple thread pools in the cluster). This is usually a good thing. However it also bears an inherent problem: consistency. Consider the parallel join after the service tasks. When execution of a service tasks is completed, we arrive at the parallel join and need to decide whether to wait for the other executions or whether we can move forward. That means, for each branch arriving at the parallel join, we need to take a decision whether we can continue or whether we need to wait for one or more other executions on the other branches. Why is this a problem? Since the service tasks are configured using an asynchronous continuation, it is possible that the corresponding jobs are all acquired at the same time and delegated to different worker threads by the JobExecutor. The consequence is that the transactions in which the services are executed and in which the 3 individual executions arrive at the parallel join can overlap. And if they do so, each individual transaction will not "see", that another transaction is arriving at the same parallel join concurrently and thus assume that it has to wait for the others. However, if each transaction assumes that it has to wait for the other ones, none will continue the process after the parallel join and the process instance will remain in that state forever. How does Activiti address this problem? Activiti performs optimistic locking. Whenever we take a decision based on data that might not be current (because another transaction might modify it before we commit, we make sure to increment the version of the same database row in both transactions). This way, whichever transaction commits first wins and the other ones fail with an optimistic locking exception. This solves the problem in the case of the process discussed above: if multiple executions arrive at the parallel join concurrently, they all assume that they have to wait, increment the version of their parent execution (the process instance) and then try to commit. Whichever execution is first will be able to commit and the other ones will fail with an optimistic locking exception. Since the executions are triggered by a job, Activiti will retry to perform the same job after waiting for a certain amount of time and hopefully this time pass the synchronizing gateway. Is this a good solution? As we have seen, optimistic locking allows Activiti to prevent inconsistencies. It makes sure that we do not "keep stuck at the joining gateway", meaning: either all executions have passed the gateway or, there are jobs in the database making sure that we retry passing it. However, while this is a perfectly fine solution from the point of view of persistence and consistency, this might not always be desirable behavior at an higher level: Activiti will retry the same job for a fixed maximum number of times only ('3' in the default configuration). After that, the job will still be present in the database but not be retried actively anymore. That means that an operator would need to trigger the job manually. If a job has non-transactional side effects, those will not be rolled back by the failing transaction. For instance, if the "book concert tickets" service does not share the same transaction as Activiti, we might book multiple tickets if we retry the job. In Activiti 5.9 we thus introduced a concept, which was already present in jBPM 4 and is called 'exclusive jobs'.
What are exclusive jobs? An exclusive job cannot be performed at the same time as another exclusive job from the same process instance. Consider the process shown above: if we declare the service tasks to be exclusive, the JobExecutor will make sure that the corresponding jobs are not executed concurrently. Instead, it will make sure that whenever it acquires an exclusive job from a certain process instance, it acquires all other exclusive jobs from the same process instance and delegates them to the same worker thread. This ensures sequential execution execution of the jobs. How can I enable this feature? Since Activiti 5.9, exclusive jobs are the default configuration. All asynchronous continuations and timer events are thus exclusive by default. In addition, if you want a job to be non-exclusive, you can configure it as such using activiti:exclusive="false". For example, the following servicetask would be asynchronous but non-exclusive. <serviceTask id="service" activiti:expression="${myService.performBooking(hotel, dates)}" activiti:async="true" activiti:exclusive="false" /> Is this a good solution? We had some people asking whether this was a good solution. Their concern was that this would to prevent you from "doing things" in parallel and would thus be a performance problem. Again, two things have to be taken into consideration: It can be turned off if you are an expert and know what you are doing (and have understood the section named "Why exclusive Jobs?"). Other than that, it is more intuitive for most users if things like asynchronous continuations and timers just work. It is actually not a performance issue. Performance is an issue under heavy load. Heavy load means that all worker threads of the job executor are busy all the time. With exclusive jobs, Activiti will simply distribute the load differently. Exclusive jobs means that jobs from a single process instance are performed by the same thread sequentially. But consider: you have more than one single process instance. And jobs from other process instances are delegated to other threads and executed concurrently. This means that with exclusive jobs Activiti will not execute jobs from the same process instance concurrently, but it will still execute multiple instances concurrently. From an overall throughput perspective this is desirable in most scenarios as it usually leads to individual instances being done more quickly. Furthermore, data that is required for executing subsequent jobs of the same process instance will already be in the cache of the executing cluster node. If the jobs do not have this node affinity, that data might need to be fetched from the database again.
Process Initiation Authorization By default everyone is allowed to start a new process instance of deployed process definitions. The process initiation authorization functionality allows to define users and groups so that web clients can optionally restrict users to start a new process instance. NOTE that the authorization definition is NOT validated by the Activiti Engine in any way. This functionality is only meant for developers to ease the implementation of authorization rules in a web client. The syntax is similar to the syntax of user assignment for a user task. A user or group can be assigned as potential initiator of a process using <activiti:potentialStarter> tag. Here is an example: <process id="potentialStarter"> <extensionElements> <activiti:potentialStarter> <resourceAssignmentExpression> <formalExpression>group2, group(group3), user(user3)</formalExpression> </resourceAssignmentExpression> </activiti:potentialStarter> </extensionElements> <startEvent id="theStart"/> ... In the above xml excerpt user(user3) refers directly to user user3 and group(group3) to group group3. No indicator will default to a group type. It is also possible to use attributes of the <process> tag, namely <activiti:candidateStarterUsers> and <activiti:candidateStarterGroups>. Here is an example: <process id="potentialStarter" activiti:candidateStarterUsers="user1, user2" activiti:candidateStarterGroups="group1"> ... It is possible to use both attributes simultaneously. After the process initiation authorizations are defined, a developer can retrieve the authorization definition using the following methods. This code retrieves the list of process definitions which can be initiated by the given user: processDefinitions = repositoryService.createProcessDefinitionQuery().startableByUser("userxxx").list(); It's also possible to retrieve all identity links that are defined as potential starter for a specific process definition identityLinks = repositoryService.getIdentityLinksForProcessDefinition("processDefinitionId"); The following example shows how to get list of users who can initiate the given process: List<User> authorizedUsers = identityService().createUserQuery().potentialStarter("processDefinitionId").list(); Exactly the same way, the list of groups that is configured as a potential starter to a given process definition can be retrieved: List<Group> authorizedGroups = identityService().createGroupQuery().potentialStarter("processDefinitionId").list();
Data objects [EXPERIMENTAL] BPMN provides the possibility to define data objects as part of a process or sub process element. According to the BPMN specification it's possible to include complex XML structures that might be imported from XSD definitions. As a first start to support data objects in Activiti the folowing XSD types are supported: <dataObject id="dObj1" name="StringTest" itemSubjectRef="xsd:string"/> <dataObject id="dObj2" name="BooleanTest" itemSubjectRef="xsd:boolean"/> <dataObject id="dObj3" name="DateTest" itemSubjectRef="xsd:datetime"/> <dataObject id="dObj4" name="DoubleTest" itemSubjectRef="xsd:double"/> <dataObject id="dObj5" name="IntegerTest" itemSubjectRef="xsd:int"/> <dataObject id="dObj6" name="LongTest" itemSubjectRef="xsd:long"/> The data object definitions will be automatically converted to process variables using the 'name' attribute value as the name for the new variable. In addition to the definition of the data object Activiti also provides an extension element to assign a default value to the variable. The following BPMN snippet provides an example: <process id="dataObjectScope" name="Data Object Scope" isExecutable="true"> <dataObject id="dObj123" name="StringTest123" itemSubjectRef="xsd:string"> <extensionElements> <activiti:value>Testing123</activiti:value> </extensionElements> </dataObject>