添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Activiti 5.3 User Guide


Table of Contents

1. Introduction
License
Download
Sources
Required software
Reporting problems
Experimental features
2. Getting Started
One minute version
Demo setup
Example projects in the workspace
Library dependencies
Eclipse setup
Inspecting the database
Database table names
3. Configuration
Creating a ProcessEngine
ProcessEngineConfiguration bean
Database configuration
Job executor activation
Mail server configuration
History configuration
Exposing configuration beans in expressions and scripts
Supported databases
Changing the database
Downloading the Oracle driver
Database upgrade
4. Spring integration
ProcessEngineFactoryBean
Transactions
Expressions
Automatic resource deployment
Unit testing
5. API
Engine API
Exception strategy
Unit testing
Debugging unit tests
The process engine in a webapplication
Process Virtual Machine API
Expressions
6. Deployment
Business archives
Deploying with Activiti Probe
Deploying programmatically
Deploying with ant
Deploying with Activiti Probe
External resources
Java classes
Using spring beans from a process
Creating a single app
Versioning of process definitions
Providing a process diagram
Generating a process diagram
7. BPMN
What is BPMN?
Examples
Defining a process
Getting started: 10 minute tutorial
Prerequisites
Goal
Use case
Process diagram
XML representation
Starting a process instance
Task lists
Claiming the task
Completing the task
Ending the process
Code overview
Future enhancements
BPMN 2.0 constructs
Custom extensions
Events
Start events
None start event
End events
None end event
Error end event
Sequence flow
Conditional sequence flow
Default sequence flow
Gateways
Exclusive gateway
Parallel Gateway
User task
Script Task
Java Service Task
WebService Task
Business Rule Task
Email task
Manual Task
Java receive task
Execution listener
Task listener
Multi-instance (for each)
Boundary events
Timer Boundary Event
Error Boundary event
Intermediate catching events
Timer intermediate catching event
SubProcess
Call activity (subprocess)
8. Forms
Build-in form rendering
External form rendering
Form properties
9. JPA
Requirements
Configuration
Usage
Simple Example
Query JPA process variables
Advanced example using Spring beans and JPA
10. History
Querying history
HistoricProcessInstanceQuery
HistoricActivityInstanceQuery
HistoricDetailQuery
HistoricTaskInstanceQuery
History configuration
History for audit purposes
11. Eclipse Designer
Installation
Activiti Designer editor features
Activiti Designer BPMN features
Activiti Designer deployment features
Extending Activiti Designer
Customizing the palette
Validating diagrams and exporting to custom output formats
12. Activiti Modeler
Changing the location of the process model repository
Changing the host of the Modeler
Configuring Apache Tomcat for Activiti Modeler
13. Activiti Cycle
Overview
Repositories
Artifacts and Actions
More Plug-Ins
14. REST API
Repository
Upload Deployment
Get Deployments
Get Deployment Resource
Delete Deployment
Delete Deployments
Engine
Get Process Engine
Processes
List Process Definitions
Get Process Definition
Get Process Definition Form
Start Process Instance
Tasks
Get Task Summary
List Tasks
Get Task
Get Task Form
Perform Task Operation
Identity
Login
Get User
List User's Groups
Get Group
List Group Users
Management
List Jobs
Get Job
Execute Job
Execute Jobs
List Database Tables
Get Table Meta Data
Get Table Data
15. Activiti Explorer
Overview
Tasks Management
Starting processes
16. Activiti Probe
Overview
Process Engine Status
Job Management
Deployments
Database
17. Activiti KickStart
Overview
Changing the database
Referencing form properties
Capture the process initiator
18. JBPM Migration
Database migration
Process conversion
Extend the migration logic

After downloading the Activiti distribution zip file from the Activiti website, follow these steps to get the demo setup running with default settings. You'll need a working Java runtime and Ant installation.

  • Unzip the Activiti distribution zip file.

  • Open up a terminal window and navigate to the setup folder in the unzipped folder.

  • Type ant demo.start and hit enter.

  • When the script finishes, it starts up all the Activiti webapps in your browser. Login with kermit/kermit.

That's it! If you want to know more about what actually happened in the steps above, read the longer version . You can also start learning all about Activiti and BPMN 2.0 in the ten minute tutorial .

'Demo Setup' is an ant script located in directory setup that sets up an Activiti environment in no time.

To run the script, you'll need a working Java runtime and Ant installation. Also make sure that the JAVA_HOME and ANT_HOME system variables are correctly set. The way to do this depends on your operating system, but the manual of ant gives a description on how to do this. The demo setup script is tested with Ant 1.7.1+.

If you don't provide any parameters, the demo setup will boot using default settings. To configure the setup for your environment, update the property files build.properties and build.{your-database}.properties . Check those files for more information on configurable properties and potential values.

The easiest way to get started is open a command prompt in de setup folder and type:

ant demo.start

This ant target will start a tomcat and a H2 database if you didn't change the default seetings. The first time when the script is executed, it will also perform following installations:

  • (*) Build the webapps. All libraries are stored in ${actviti.home}/setup/files/dependencies/libs The webapps without the libs are stored in ${actviti.home}/setup/files/webapps . Building the webapps means that webapps are combined with the necessary libraries in ${actviti.home}/setup/build/webapps

  • (*) Install the H2 in ${activiti.home}/apps/h2. This only happens if you're using h2 as your database. H2 is the default database.

  • Start the H2 database. Again, this is only done if using h2 as the database. If you're using a different database it is assumed that it is already up and running.

  • (*) Create the Activiti tables in the database

  • (*) Insert the demo users and groups in the Activiti identity tables (see below)

  • (*) Deploy the example processes to the Activiti Engine DB

  • (*) Download Tomcat if not available in the ${downloads.dir}

  • (*) Install Tomcat in ${activiti.home}/apps/apache-tomcat-${tomcat.version}

  • (*) Create an Activiti configuration jar

  • (*) Deploy the REST interface webapp into tomcat

  • (*) Download the Activiti Modeler webapp to ${activiti.home}/webapps. The license for the Activiti Modeler is MIT .

  • (*) Deploy the Probe, Explorer and Modeler webapps into tomcat.

  • Start tomcat

(*) only performed the first time when running ant demo.start

After running this target H2 and Tomcat will be running in the background. To stop those processes run ant demo.stop .

The other targets in that build script can also be called individually and they will take the configurable properties into account. Run ant -p for more details.

These are the demo users:


Now you can access following web applications:

Table 2.2. The webapp tools

Webapp Name URL Description
Activiti Probe http://localhost:8080/activiti-probe The admin management console. Use this tool to see if the configured process engine is correctly initialized, DB tables contents.
Activiti Explorer http://localhost:8080/activiti-explorer The process engine user console. Use this tool to view your personal and candidate task lists and to complete tasks.
Activiti Cycle http://localhost:8080/activiti-cycle The Activiti collabotation tool. Use this to browse repositories and execute transformations between model formats.
Activiti Modeler powered by Signavio http://localhost:8080/activiti-modeler The web based process designer tool. Use this tool to graphically author BPMN 2.0 compliant process definitions files.
Activiti KickStart http://localhost:8080/activiti-kickstart Allows to specify processes quickly and efficiently in an adhoc way. Simple processes, quick prototypes and adhoc workflow are created in no time using KickStart.
Activiti Administrator http://localhost:8080/activiti-administrator A webapp to administer the users and groups. Currently this is realized as a seperate app, but we plan to unify some of the webapps into a single webapp with authorization. Note that the Activiti demo setup is a way of showing the capabilities and functionality of Activiti as easy and as fast as possible. This does however, not mean that it is the only way of using Activiti. As Activiti is 'just a jar', it can be embedded in any Java environment: with swing or on a Tomcat, JBoss, WebSphere, etc. Or you could very well choose to run Activiti as a typical, standalone BPM server. If it is possible in Java, it is possible with Activiti!

The distribution contains a workspace directory containing a couple of example java projects:

the section called “Eclipse setup” shows how you can set up your eclipse environment to play with these example projects.

As part of the demo.start , the examples will be inflated. This means that all the libs and configuration files will be put in place. If you don't run the demo.start and you want to inflate the examples with libs in the appropriate place, run this command in the setup directory:

ant inflate.examples

Once you've done that the activiti-engine-examples and activiti-spring-examples will contain libs-runtime and libs-test directories containing the runtime dependency jars and test time dependency jars respectively.

In order to prevent that the distribution file becomes too big by libraries that are included multiple times, all the libraries are grouped into a single directory somewhere in the setup/files .

The ant scripts in the setup/build.xml can inflate the examples (target inflate.examples ) with the libs and they will include the appropriate libs when building the webapps.

All the libs are located in setup/files/dependencies/libs And the following files in setup/files/dependencies describe the library dependencies:

The Activiti process engine is configured through a xml file called activiti.cfg.xml . Note that this is not applicable if you're using the Spring style of building a process engine . The easiest way to obtain a ProcessEngine , is to use the org.activiti.engine.ProcessEngines class:

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine()

This will look for an activiti.cfg.xml file on the classpath and construct an engine based on the configuration in that file. The following snippet shows an example configuration. The following sections will give a detailed overview of the configuration properties.

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
    <property name="jdbcUrl" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
    <property name="jdbcDriver" value="org.h2.Driver" />
    <property name="jdbcUsername" value="sa" />
    <property name="jdbcPassword" value="" />
    <property name="databaseSchemaUpdate" value="true" />
    <property name="jobExecutorActivate" value="false" />
    <property name="mailServerHost" value="mail.my-corp.com" /> 
    <property name="mailServerPort" value="5025" />    
  </bean>
</beans>

Note that the configuration xml is in fact a Spring configuration. This does not mean that Activiti can only be used in a Spring environment! We are simply leveraging the parsing and dependency injection capabilitities of Spring internally for building up the engine. The ProcessEngineConfiguration object can also be created programmatically using the configuration file. It is also possible to use a different bean id (eg. see line 3). ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault(); ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource); ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource, String beanName); ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(InputStream inputStream); ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(InputStream inputStream, String beanName);

It is also possible not to use a configuration file, and create a configuration based on defaults (see the different supported classes for more information). ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration(); ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();

All these ProcessEngineConfiguration.createXXX() methods return a ProcessEngineConfiguration that can further be tweaked if needed. After calling the buildProcessEngine() operation, a ProcessEngine is created: ProcessEngine processEngine = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration() .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE) .setJdbcUrl("jdbc:h2:mem:my-own-db;DB_CLOSE_DELAY=1000") .setJobExecutorActivate(true) .buildProcessEngine();

The activiti.cfg.xml must contain a bean that has the id 'processEngineConfiguration' .

 <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

This bean is then used to construct the ProcessEngine . There are multiple classes available that can be used to define the processEngineConfiguration . These classes represent different environments, and set defaults accordingly. It's a best practice to select the class the matches (the most) your environment, to minimalise the number of properties needed to configure the engine. Following classes are currently available (more will follow in future releases): org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration : the process engine is used in a standalone way. Activiti will take care of the transactions. By default, the database will only be checked when the engine boots (and an exception is thrown if there is no Activiti schema or the schema version is incorrect). org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration : this is a convience class for unit testing purposes. Activiti will take care of the transactions. An H2 in-memory database is used by default. The database will be created and dropped when the engine boots and shuts down. When using this, probably no additional configuration is needed (except when using for example the job executor or mail capabilities). org.activiti.spring.SpringProcessEngineConfiguration : To be used when the process engine is used in a Spring environment. See the Spring integration section for more information. org.activiti.engine.impl.cfg.JtaProcessEngineConfiguration : ( [EXPERIMENTAL] ) to be used when the engine runs in standalone mode, with JTA transactions.

There are two ways to configure the database that the Activiti engine will use. The first option is to define the jdbc properties of the database: jdbcUrl : jdbc url of the database. jdbcDriver : implementation of the driver for the specific database type. jdbcUsername : username to connect to the database. jdbcPassword : password to connect to the database. The datasource that is constructed based on the provided jdbc properties will have the default MyBatis connection pool settings. Following attributes can optionally be set to tweak that connection pool (taken from the MyBatis documentation): jdbcMaxActiveConnections : The number of active connections that the connection pool at maximum at any time can contain. Default is 10. jdbcMaxIdleConnections : The number of idle connections that the connection pool at maximum at any time can contain. jdbcMaxCheckoutTime : The amount of time in milliseconds a connection can be 'checked out' from the connection pool before it is forcefully returned. Default is 20000 (20 seconds). jdbcMaxWaitTime : This is a low level setting that gives the pool a chance to print a log status and re-attempt the acquisition of a connection in the case that it’s taking unusually long (to avoid failing silently forever if the pool is misconfigured) Default is 20000 (20 seconds). Example database configuration: <property name="jdbcUrl" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" /> <property name="jdbcDriver" value="org.h2.Driver" /> <property name="jdbcUsername" value="sa" /> <property name="jdbcPassword" value="" /> Alternatively, a javax.sql.DataSource implementation can be used (eg. DBCP from Apache Commons ): <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" > <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/activiti" /> <property name="username" value="activiti" /> <property name="password" value="activiti" /> <property name="defaultAutoCommit" value="false" /> </bean> <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> <property name="dataSource" ref="dataSource" /> Note that Activiti does not ship with a library that allows to define such datasource. So you have to make sure that the libraries (eg. from DBCP) are on your classpath. Following properties can be set, regardless of using the jdbc or datasource approach: databaseType : It's normally not necessary to specify this property as it is automatically analysed from the database connection metadata. Should only be specified in case automatic detection fails. Possible values: {h2, mysql, oracle, postgres, mssql, db2} This property is required when not using the default H2 database This setting will determine which create/drop scripts and queries will be used. See the 'supported databases' section for an overview of which types are supported. databaseSchemaUpdate : allows to set the strategy to handle the database schema on process engine boot and shutdown. false (default): Checks the version of the DB schema against the library when the process engine is being created and throws an exception if the versions don't match. true : Upon building of the process engine, a check is performed and an update of the schema is performed if it is necessary. If the schema doesn't exist, it is created. create-drop : Creates the schema when the process engine is being created and drops the schema when the process engine is being closed.

When you want to run the demo setup using oracle as datasource, an extra step is required BEFORE you call the ant target demo.start . Since we cannot redistribute the Oracle JDBC driver due to its licence, you should download it manually: http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html . Make sure you download ojdbc5.jar (our tests run against 10g ojdbc using version 11.2.0.1). Copy the downloaded ojdbc5.jar to setup/files/dependencies/libs/ . Make sure the filename is 'ojdbc5.jar' in order to be picked up by the activiti demo installation (eg. when taking the driver from your maven repo).

[EXPERIMENTAL]

We don't yet have enough coverage testing of upgrade to have full confidence in it. That is why we still marked it as experimental. Make sure you make a backup of your database (using your database backup capabilities) before you run an upgrade.

By default, a version check will be performed each time a process engine is created. This typically happens once at boot time of your application or of the Activiti webapps. If the Activiti library notices a difference between the library version and the version of the Activiti database tables, then an exception is thrown.

To upgrade, you have to start with putting the following configuration property in your activiti.cfg.xml configuration file:

<beans ... >
  <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
    <!-- ... -->
    <property name="databaseSchemaUpdate" value="true" />    
    <!-- ... -->
  </bean>
</beans>

Then just upgrade the Activiti libraries in your application. Or start up a new version of Activiti and point it to an database that contains an older version. With databaseSchemaUpdate set to true , Activiti will automatically upgrade the DB schema to the newer version the first time when it notices that libraries and DB schema are out of sync.

While you definitely can use Activiti without Spring, we've provided some very nice integration features that are explained in this chapter.

The ProcessEngine can be configured as a regular Spring bean. The starting point of the integration is the class org.activiti.spring.ProcessEngineFactoryBean . That bean takes a process engine configuration and creates the process engine. This means that the way and all configuration properties documented in the configuration section are exactly the same as for Spring: <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> </bean> <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean"> <property name="processEngineConfiguration" ref="processEngineConfiguration" /> </bean> Do note that the processEngineConfiguration bean now uses the org.activiti.spring.SpringProcessEngineConfiguration class.

We'll explain the SpringTransactionIntegrationTest found in the spring examples of the distribution step by step. Here is the spring configuration file that we use in this example (located in SpringTransactionIntegrationTest-context.xml). The quoted section contains the dataSource, transactionManager, processEngine and the Activiti Engine services. When passing the DataSource to the SpringProcessEngineConfiguration (using property "dataSource"), Activiti uses a org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy internally, which wraps the passed DataSource. This is done make sure the SQL connections retrieved from the DataSource and the Spring transactions play well together. This implies that it's no longer needed to proxy the dataSource yourself in spring-configuration, however it's allowed to pass a TransactionAwareDataSourceProxy into the SpringProcessEngineConfiguration . In this case no additional wrapping will occur. Make sure when declaring a TransactionAwareDataSourceProxy in Spring configuration yourself, that you don't use it for resources that are already aware of Spring-transactions (eg. DataSourceTransactionManager and JPATransactionManager need the un-proxied dataSource). <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driverClass" value="org.h2.Driver" /> <property name="url" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" /> <property name="username" value="sa" /> <property name="password" value="" /> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> <property name="dataSource" ref="dataSource" /> <property name="transactionManager" ref="transactionManager" /> <property name="databaseSchemaUpdate" value="true" /> <property name="jobExecutorActivate" value="false" /> </bean> <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean"> <property name="processEngineConfiguration" ref="processEngineConfiguration" /> </bean> <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" /> <bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" /> <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" /> <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" /> <bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" /> ...

The remainder of that spring configuration file contains the beans and configuration that we'll use in this particular example:

<beans>  
  <tx:annotation-driven transaction-manager="transactionManager"/>
  <bean id="userBean" class="org.activiti.spring.test.UserBean">
    <property name="runtimeService" ref="runtimeService" />
  </bean>
  <bean id="printer" class="org.activiti.spring.test.Printer" />
</beans>

First the application context is created with any of the Spring ways to do that. In this example you could use a classpath XML resource to configure our Spring application context:

ClassPathXmlApplicationContext applicationContext = 
    new ClassPathXmlApplicationContext("org/activiti/examples/spring/SpringTransactionIntegrationTest-context.xml");

or since it is a test:

@ContextConfiguration("classpath:org/activiti/spring/test/transaction/SpringTransactionIntegrationTest-context.xml")

Then we can get the service beans and invoke methods on them. The ProcessEngineFactoryBean will have added an extra interceptor to the services that applies Propagation.REQUIRED transaction semantics on the Activiti service methods. So we can use for example the repositoryService to deploy a process like this:

RepositoryService repositoryService = (RepositoryService) applicationContext.getBean("repositoryService");
String deploymentId = repositoryService
  .createDeployment()
  .addClasspathResource("org/activiti/spring/test/hello.bpmn20.xml")
  .deploy()
  .getId();
    

The other way around also works. In this case, the Spring transaction will be around the userBean.hello() method and the Activiti service method invocation will join that same transaction.

UserBean userBean = (UserBean) applicationContext.getBean("userBean");
userBean.hello();

The UserBean looks like this. Remember from above in the Spring bean configuration we injected the repositoryService into the userBean.

public class UserBean {
  /** injected by Spring */
  private RuntimeService runtimeService;
  @Transactional
  public void hello() {
    // here you can do transactional stuff in your domain model
    // and it will be combined in the same transaction as 
    // the startProcessInstanceByKey to the Activiti RuntimeService
    runtimeService.startProcessInstanceByKey("helloProcess");
  public void setRuntimeService(RuntimeService runtimeService) {
    this.runtimeService = runtimeService;
}

When using the ProcessEngineFactoryBean, by default, all expressions in the BPMN processes will also 'see' all the Spring beans. It's possible to limit the beans you want to expose in expressions or even exposing no beans at all using a map that you can configure. The example below exposes a single bean (printer), available to use under the key "printer". To have NO beans exposed at all, just pass an empty list as 'beans' property on the SpringProcessEngineConfiguration. When no 'beans' property is set, all spring-beans in the context will be available. <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> <property name="beans"> <entry key="printer" value-ref="printer" /> </property> </bean> <bean id="printer" class="org.activiti.examples.spring.Printer" />

Now the exposed beans can be used in expressions: for example, the SpringTransactionIntegrationTest hello.bpmn20.xml shows how a method on a Spring bean can be invoked using a UEL method expression:

<definitions id="definitions" ...>
  <process id="helloProcess">
    <startEvent id="start" />
    <sequenceFlow id="flow1" sourceRef="start" targetRef="print" />
    <serviceTask id="print" activiti:expression="#{printer.printMessage()}" />
    <sequenceFlow id="flow2" sourceRef="print" targetRef="end" />
    <endEvent id="end" />
  </process>
</definitions>

Where Printer looks like this:

public class Printer {
  public void printMessage() {
    System.out.println("hello world");
}

And the Spring bean configuration (also shown above) looks like this:

<beans ...>
  <bean id="printer" class="org.activiti.examples.spring.Printer" />
</beans>

When integrating with Spring, business processes can be tested very easily using the standard Activiti testing facilities . Following example shows how a business process is tested in a typical Spring-based unit test: @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:org/activiti/spring/test/junit4/springTypicalUsageTest-context.xml") public class MyBusinessProcessTest { @Autowired private RuntimeService runtimeService; @Autowired private TaskService taskService; @Autowired @Rule public ActivitiRule activitiSpringRule; @Test @Deployment public void simpleProcessTest() { runtimeService.startProcessInstanceByKey("simpleProcess"); Task task = taskService.createTaskQuery().singleResult(); assertEquals("My Task", task.getName()); taskService.complete(task.getId()); assertEquals(0, runtimeService.createProcessInstanceQuery().count()); Note that for this to work, you need to define a org.activiti.engine.test.ActivitiRule bean in the Spring configuration (which is injected by auto-wiring in the example above). <bean id="activitiRule" class="org.activiti.engine.test.ActivitiRule"> <property name="processEngine" ref="processEngine" /> </bean>

The engine API is the most common way of interacting with Activiti. The central starting point is the ProcessEngine , which can be created in several ways as described in the configuration section . From the ProcessEngine, you can obtain the various services that contain the workflow/BPM methods. ProcessEngine and the services objects are thread safe. So you can keep a reference to 1 of those for a whole server.

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
ManagementService managementService = processEngine.getManagementService();
IdentityService identityService = processEngine.getIdentityService();
HistoryService historyService = processEngine.getHistoryService();
FormService formService = processEngine.getFormService();

The names of the service are quite self-explanatory. For detailed information on the services and the engine API, see the javadocs .

ProcessEngines.getDefaultProcessEngine() will initialize and build a process engine the first time it is called and afterwards always return the same process engine. Proper creation and closing of all process engines can be done with ProcessEngines.init() and ProcessEngines.destroy()

ProcessEngines will scan for all activiti.cfg.xml and activiti-context.xml files. For all activiti.cfg.xml files, the process engine will be build in the typical Activiti way: ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(inputStream).buildProcessEngine() . For all activiti-context.xml files, the process engine will be build in the Spring way: First the spring application context is created and then the process engine is obtained from that application context.

The base exception in Activiti is the org.activiti.engine.ActivitiException , an unchecked exception. This exception can be thrown at all times by the API, but 'expected' exceptions that happen in specific methods are documented in the the javadocs . For example, an extract from TaskService : * Called when the task is successfully executed. * @param taskId the id of the task to complete, cannot be null. * @throws ActivitiException when no task exists with the given id. void complete(String taskId); In the example above, when an id is passed for which no task exists, an exception will be thrown. Also, since the javadoc explicitly states that taskId cannot be null, an ActivitiException will be thrown when null is passed . Even though we want to avoid a big exception hierarchy, the following subclasses were added which are thrown in specific cases: ActivitiWrongDbException: Thrown when the Activiti engine discovers a mismatch between the database schema version and the engine version. ActivitiOptimisticLockingException: Thrown when an optimistic locking occurs in the datastore caused by concurrent access of the same data entry. ActivitiClassLoadingException: Thrown when an class requested to load was not found or when error occurred while loading it (eg. JavaDelegates, TaskListeners, ...).

Business processes are an integral part of software projects and they should be tested in the same way normal application logic is tested: with unit tests. Since Activiti is an embeddable Java engine, writing unit test for business processes is as simple as writing regular unit tests. Activiti supports both Junit versions 3 and 4 style of unit testing. In the Junit 3 style, the org.activiti.engine.test.ActivitiTestCase must be extended. This will make the processEngine and the services available through protected member fields. In the setup() of the test, the processEngine will be initialized by default with the activiti.cfg.xml resource on the classpath. To specify a different configuration file, override the getConfigurationResource() method. Process engines are be cached statically over multiple unit tests when the configuration resource is the same. By extending ActivitiTestCase , you can annotate test methods with org.activiti.engine.test.Deployment . Before the test is run, a resource file of the form testClassName.testMethod.bpmn20.xml in the same package as the test class, will be deployed. At the end of the test, the deployment will be deleted, including all related process instances, tasks, etc. The Deployment annotation also supports setting the resource location explicitly. See the Javadocs for more details. Taking all that in account, a Junit 3 style test looks as follows. public class MyBusinessProcessTest extends ActivitiTestCase { @Deployment public void testSimpleProcess() { runtimeService.startProcessInstanceByKey("simpleProcess"); Task task = taskService.createTaskQuery().singleResult(); assertEquals("My Task", task.getName()); taskService.complete(task.getId()); assertEquals(0, runtimeService.createProcessInstanceQuery().count()); To get the same functionality when using the Junit 4 style of writing unit tests, the org.activiti.engine.test.ActivitiRule Rule must be used. Through this rule, the process engine and services are available through getters. As with the ActivitiTestCase (see above), including this Rule will enable the use of the org.activiti.engine.test.Deployment annotation (see above for an explanation of its use and configuration) and it will look for the default configuration file on the classpath. Process engines are statically cached over multiple unit tests when using the same configuration resource. Following code snippet shows an example of using the Junit 4 style of testing and the usage of the ActivitiRule . public class MyBusinessProcessTest { @Rule public ActivitiRule activitiRule = new ActivitiRule(); @Test @Deployment public void ruleUsageExample() { RuntimeService runtimeService = activitiRule.getRuntimeService(); runtimeService.startProcessInstanceByKey("ruleUsage"); TaskService taskService = activitiRule.getTaskService(); Task task = taskService.createTaskQuery().singleResult(); assertEquals("My Task", task.getName()); taskService.complete(task.getId()); assertEquals(0, runtimeService.createProcessInstanceQuery().count());

[EXPERIMENTAL] This API is probably going to change in subsequent releases.

The Process Virtual Machine API exposes the POJO core of the Process Virtual Machine. Reading and playing with it is interesting for education purposes to understand the internal workings of Activiti. And the POJO API can also be used to build new process languages.

For example:

PvmProcessDefinition processDefinition = new ProcessDefinitionBuilder()
  .createActivity("a")
    .initial()
    .behavior(new WaitState())
    .transition("b")
  .endActivity()
  .createActivity("b")
    .behavior(new WaitState())
    .transition("c")
  .endActivity()
  .createActivity("c")
    .behavior(new WaitState())
  .endActivity()
  .buildProcessDefinition();
PvmProcessInstance processInstance = processDefinition.createProcessInstance();
processInstance.start();
PvmExecution activityInstance = processInstance.findExecution("a");
assertNotNull(activityInstance);
activityInstance.signal(null, null);
activityInstance = processInstance.findExecution("b");
assertNotNull(activityInstance);
activityInstance.signal(null, null);
activityInstance = processInstance.findExecution("c");
assertNotNull(activityInstance);

Activiti uses UEL for expression-resolving. UEL stands for Unified Expression Language and is part of the EE6 specification ()see the EE6 specification for detailed information). To support all features of latest UEL spec on ALL environements, we use a modified version of JUEL. Expressions can be used in for example Java Service tasks , Execution Listeners , Task Listeners and Conditional sequence flows . Although there are 2 types of expressions, value-expression and method-expression, activiti makes abstraction of this and they can both be used where an expression is needed. Value expression : resolves to a value. By default, all process variables are available to use. Also all spring-beans (if using Spring) are available to use in expressions. On top of that, the DelegateExecution is also available in the expression-context and can be accessed using the name execution . Since the execution is exposed as execution , all variables and spring-beans with name execution are hidden and cannot be used in an expression. Some examples:

${myVar}
${myBean.myProperty}

Method expression : invokes a method, with or without parameters. When invoking a method without parameters, be sure to add empty parentheses after the method-name. The passed parameters can be literal values or expressions that are resolved themselves. Examples:

${printer.print()}
${myBean.addNewOrder('orderName')}
${myBean.doSomething(myVar, execution)}
      Note that these expressions support resolving primitives (incl. comparing them), beans, lists, arrays and maps.
      For more concrete usage and examples, check out Expressions in Spring, Java Service tasks, Execution Listeners,  Task Listeners or Conditional sequence flows.
    

To deploy processes, they have to be wrapped in a business archive. A business archive is the unit of deployment to an Activiti Engine. Basically a business archive is equivalent to a zip file. It can contain BPMN 2.0 processes, task forms, rules and any other type of file. In general, a business archive contains a collection of named resources.

When a business archive is deployed, it is scanned for BPMN files with a .bpmn20.xml extension. Each of those will be parsed and potentially contains multiple process definitions. Note that Java classes present in the business archive will not be added to the classpath. All custom classes used in process definitions in the business archive (for example Java service tasks or event listener implementations) should be present on the activiti-engine's classpath in order to run the processes.

In page 'Deployments' in Activiti Probe , you can upload a business archive to deploy it in the Activiti Engine.

Process definitions live in the Activiti database. Those process definitions can reference delegation classes when using serviceTasks or execution listeners or spring beans from the activiti configuration file. These classes and the spring configuration file have to be available to all process engines.

When expressions or scripts use spring beans, those spring beans have to be available to the engine. If you are building your own webapp and you configure your process engine in your context as described in the spring integration section , that is straightforward. But bear in mind that you also should update the Activiti rest webapp with that context if you use it. You can do that by replacing the activiti.cfg.xml in the ${activiti.home}/apps/apache-tomcat-6.0.29/webapps/activiti-rest/lib/activiti-cfg.jar with a activiti-context.xml containing your spring context configuration.

BPMN doesn't have a notion of versioning. And that is good because the executable BPMN process file will probably live in an SVN repository as part of your development project. Versions of process definitions are created during deployment. During deployment, Activiti will assign a version to the ProcessDefinition before it is stored in the Activiti DB.

For each process definition in a business archive the following steps are performed to initialize the properties key , version , name and id :

A process diagram image can be added to a deployment. This image will be stored in the Activiti repository and is accessible through the API. Tis image is also used to visualize the process in Activiti Explorer. Suppose we have a process on our classpath, org/activiti/expenseProcess.bpmn20.xml that has as process key 'expense'. Following naming conventions for the process diagram image apply (in this specific order): If in the deployment an image resource with as name the BPMN 2.0 xml file name concatenated with the process key and an image suffix exists, this image is used. In our example, this would be org/activiti/expenseProcess.expense.png (or .jpg/gif). In case you have multiple images defined in one BPMN 2.0 xml file, this approach makes most sense. Each diagram image will then have the process key in its file name. If no such image exists, am image resource in the deployment matching the name of the BPMN 2.0 xml file is searched. In our example this would be org/activiti/expenseProcess.png . Note that this means that every process definition defined in the same BPMN 2.0 file has the same process diagram image. In case there is only one process definition in each BPMN 2.0 xml file, this is of course no problem. Example when deploying programmatically: repositoryService.createDeployment() .name("expense-process.bar") .addClasspathResource("org/activiti/expenseProcess.bpmn20.xml") .addClasspathResource("org/activiti/expenseProcess.png") .deploy();

The image resource can then later be retrieved through the API: ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() .processDefinitionKey("expense") .singleResult(); String diagramResourceName = processDefinition.getDiagramResourceName(); InputStream imageStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), diagramResourceName);

In case no image is provided in the deployment, as described in the previous section , the Activiti engine will generate a diagram image when the process definition contains the necessary 'diagram interchange' information. This is the case when using the Activiti Modeler (and in the near future when using the Activiti Eclipse Designer). The resource can be retrieved in exactly the same way as when an image is provided in the deployment.

To create a new BPMN 2.0 process definition, it's best to have your Eclipse properly set up . Create a new XML file ( rightclick on any project and select New->Other->XML-XML File ) and give it a name. Make sure that the file ends with .bpmn20.xml , since otherwise the engine won't pick up this file for deployment. The root element of the BPMN 2.0 schema is the definitions element. Within this element, multiple process definitions can be defined (although we advise to have only one process definition in each file, since this simplifies maintenance later in the development process). An empty process definition looks as follows. Note that the minimal definitions element only needs the xmlns and targetNamespace declaration. The targetNamespace can be anything, and is useful for categorizing process definitions. <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" targetNamespace="Examples"> <process id="myProcess" name="My First Process"> </process> </definitions>

Optionally you can also add the online schemalocation of the BPMN 2.0 xsd schema, as an alternative to the XML catalog configuration in Eclipse . xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100501/BPMN20.xsd

The process element has two attributes: id : this attribute is required and maps to the key property of an Activiti ProcessDefinition object. This id can then be used to start a new process instance of the process definition, through the startProcessInstanceByKey method on the RuntimeService . This method will always take the latest deployed version of the process definition.

ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess");

Important to note here is that this is not the same as calling the startProcessInstanceById method. This method expects the String id that was generated at deploy time by the Activiti engine, and can be retrieved by calling the processDefinition.getId() method. The format of the generated id is 'key:version' , and the length is constrained to 64 characters . If you get an ActivitiException stating that the generated id is too long, limit the text in the key field of the process. name : this attribute is optional and maps to the name property of a ProcessDefinition . The engine itself doesn't use this property, so it can for example be used for displaying a more human-friendly name in a user interface.

In this section we will cover a (very simple) business process that we will use to introduce some basic Activiti concepts and the Activiti API.

This tutorial assumes that you have the Activiti demo setup running . Optionally, you should also have an Eclipse installed and imported the Activiti examples . The goal of this tutorial is to learn about Activiti and some basic BPMN 2.0 concepts. The end result will be a simple Java SE program that deploys a process definition, and interacts with this process through the Activiti engine API. We'll also touch some of the tooling around Activiti. Of course, what you'll learn in this tutorial can also be used when building your own web applications around your business processes.

The business process as described above, can be graphically visualized using the Activiti Modeler or the Activiti Designer . However, for this tutorial we'll type the XML ourselves, as this learns us the most at this point. The graphical BPMN 2.0 notation of our process looks like this: What we see is a none start event (circle on the left), followed by two user tasks : 'Write monthly financial report' and 'Verify monthly financial report' , ending in a none end event (circle with thick border on the right).

The XML version of this business process ( FinancialReportProcess.bpmn20.xml ) looks as shown below. It's easy to recognize the main elements of our process (click on the links for going to the detailed section of that BPMN 2.0 construct): The (none) start event learns us what the entry point to the process is. The user tasks declarations are the representation of the human tasks of our process. Note that the first task is assigned to the accountancy group, while the second task is assigned to the management group. See the section on user task assignment for more information on how users and groups can be assigned to user tasks. The process ends when the none end event is reached. The elements are connected with each other through sequence flows . These sequence flow have a source and target , defining the direction of the sequence flow. <definitions id="definitions" targetNamespace="http://activiti.org/bpmn20" xmlns:activiti="http://activiti.org/bpmn" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"> <process id="financialReport" name="Monthly financial report reminder process"> <startEvent id="theStart" /> <sequenceFlow id='flow1' sourceRef='theStart' targetRef='writeReportTask' /> <userTask id="writeReportTask" name="Write monthly financial report" > <documentation> Write monthly financial report for publication to shareholders. </documentation> <potentialOwner> <resourceAssignmentExpression> <formalExpression>accountancy</formalExpression> </resourceAssignmentExpression> </potentialOwner> </userTask> <sequenceFlow id='flow2' sourceRef='writeReportTask' targetRef='verifyReportTask' /> <userTask id="verifyReportTask" name="Verify monthly financial report" > <documentation> Verify monthly financial report composed by the accountancy department. This financial report is going to be sent to all the company shareholders. </documentation> <potentialOwner> <resourceAssignmentExpression> <formalExpression>management</formalExpression> </resourceAssignmentExpression> </potentialOwner> </userTask> <sequenceFlow id='flow3' sourceRef='verifyReportTask' targetRef='theEnd' /> <endEvent id="theEnd" /> </process> </definitions>

We now have created the process definition of our business process. From such a process definition, we can create process instances . In this case, one process instance would match with the creation and verification of the financial report every month. All the process instances share the same process definition. To be able to create process instances from a given process definition, we must first deploy this process definition. Deploying a process definition means two things: The process definition will be stored in the persistent datastore that is configured for your Activiti engine. So by deploying our business process, we make sure that the engine will find the process definition after an engine reboot. The BPMN 2.0 process file will be parsed to an in-memory object model that can be manipulated through the Activiti API. More information on deployment can be found in the dedicated section on deployment . As described in that section, deployment can happen in several ways. One way is through the API as follows. Note that all interaction with the Activiti engine happens through its services .

Deployment deployment = repositoryService.createDeployment()
  .addClasspathResource("FinancialReportProcess.bpmn20.xml")
  .deploy();

Now we can start a new process instance using the id we defined in the process definition (see process element in the XML file). Note that this id in Activiti terminology is called the key .

ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("financialReport");

This will create a process instance that will go first through the start event. After the start event, it follows all the outgoing sequence flow (only one in this case) and the first task ('write monthly financial report') is reached. The Activiti engine will now store a task in the persistent database. At this point, the user or group assignments attached to the task are resolved and also stored in the database. It's important to note that the Activiti engine will continue process execution steps until it reaches a wait state , such as the user task. At such a wait state, the current state of the process instance is stored in the database. It remains in that state until a user decides to complete its task. At that point, the engine will continue until it reaches a new wait state or the end of the process. When the engine reboots or crashes in the meantime, the state of the process is safe and well in the database. After the task is created, the startProcessInstanceByKey method will return since the user task activity is a wait state , In this case, the task is assigned to a group, which means that the every member of the group is a candidate to perform the task. We can now throw this all together and create a simple Java program. Create a new Eclipse project and add the Activiti jars and dependencies to its classpath (these can be found setup/files/dependencies/libs ). Before we can call the Activiti services, we must first construct a ProcessEngine that gives us access to the services. Here we use the 'standalone' configuration, that constructs a ProcessEngine that uses the database also used in the demo setup. You can download the process definition XML here . This file contains the XML as shown above, but contains also the necessary BPMN diagram interchange information to visualize the process in the Activiti tools. public static void main(String[] args) { // Create Activiti process engine ProcessEngine processEngine = ProcessEngineConfiguration .createStandaloneProcessEngineConfiguration() .buildProcessEngine(); // Get Activiti services RepositoryService repositoryService = processEngine.getRepositoryService(); RuntimeService runtimeService = processEngine.getRuntimeService(); // Deploy the process definition repositoryService.createDeployment() .addClasspathResource("FinancialReportProcess.bpmn20.xml") .deploy(); // Start a process instance runtimeService.startProcessInstanceByKey("financialReport");

We can now retrieve this task through the taskService by adding following logic:

List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("kermit").list();

Note that the user we pass to this operation needs to be a member of the accountancy group, since that was declared in the process definition: <potentialOwner> <resourceAssignmentExpression> <formalExpression> accountancy </formalExpression> </resourceAssignmentExpression> </potentialOwner>

We could also use the task query API to get the same results using the name of the group. We can now add following logic to our code: TaskService taskService = processEngine.getTaskService(); List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list();

Since we've configured our ProcessEngine to use the same database as the demo setup is using, we can now log into Activiti Explorer (login with fozzie/fozzie), and we fill find that we can start our business process after selecting the Processes page and and clicking on the 'Start Process' link in the 'Actions' column corresponding to the 'Monthly financial report' process. As explained, the process will execute up to the first user task. Since we're logged in as kermit, we can see that there is a new candidate task available for him after we've started a process instance. Select the Tasks page to view this new task. Note that even if the process was started by someone else, the task would still be visible as a candidate task to everyone in the accountancy group.

Taking all the snippets from previous sections, and you should have something like this (this code takes in account that you probably will have started a few process instances through the Activiti Explorer UI. As such, it always retrieves a list of tasks instead of one task, so it works always): public class TenMinuteTutorial { public static void main(String[] args) { // Create Activiti process engine ProcessEngine processEngine = ProcessEngineConfiguration .createStandaloneProcessEngineConfiguration() .buildProcessEngine(); // Get Activiti services RepositoryService repositoryService = processEngine.getRepositoryService(); RuntimeService runtimeService = processEngine.getRuntimeService(); // Deploy the process definition repositoryService.createDeployment() .addClasspathResource("FinancialReportProcess.bpmn20.xml") .deploy(); // Start a process instance String procId = runtimeService.startProcessInstanceByKey("financialReport").getId(); // Get the first task TaskService taskService = processEngine.getTaskService(); List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list(); for (Task task : tasks) { System.out.println("Following task is available for accountancy group: " + task.getName()); // claim it taskService.claim(task.getId(), "fozzie"); // Verify Fozzie can now retrieve the task tasks = taskService.createTaskQuery().taskAssignee("fozzie").list(); for (Task task : tasks) { System.out.println("Task for fozzie: " + task.getName()); // Complete the task taskService.complete(task.getId()); System.out.println("Number of tasks for fozzie: " + taskService.createTaskQuery().taskAssignee("fozzie").count()); // Retrieve and claim the second task tasks = taskService.createTaskQuery().taskCandidateGroup("management").list(); for (Task task : tasks) { System.out.println("Following task is available for accountancy group: " + task.getName()); taskService.claim(task.getId(), "kermit"); // Completing the second task ends the process for (Task task : tasks) { taskService.complete(task.getId()); // verify that the process is actually finished HistoryService historyService = processEngine.getHistoryService(); HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult(); System.out.println("Process instance end time: " + historicProcessInstance.getEndTime()); This code is also available as a unit test shipped with the examples (yes, you should unit test processed!).

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!.

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: 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" />

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 
           Chapter 8, Forms
         

When process execution arrives in an error end event , the, the current path of execution is ended and an error is thrown. This error can catched by a matching intermediate boundary error event . In case no matching boundary error event is found, the execution semantics default to the none end event semantics. 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 .

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) :

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>

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:

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 . Task that have an assignee are not visible in the task lists of other people, and are found in the so-called personal task list of the assignee. 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 the 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>

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. 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 attirbutes 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");

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). The Groovy jar is shipped by default with the Activiti distribution. 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.

There are 4 ways of declaring how to invoke Java logic:

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.

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.

[EXPERIMENTAL] 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. 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 (ie 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 Probe to deploy processes, the classpath does not contain the referenced classes.

It's possible to inject values into the fields of the delegated classes. The following types of injection are supported:

If available, the value is injected through a public setter method on your delegated class, folowing the Java Bean naming conventions (eg. field fistName 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. 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.impl.el.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.impl.el.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.

[EXPERIMENTAL]

To use a WebService 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 WebService: <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 WebService 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 WebService task by using the ##WebService implementation and a reference to the WebService operation. <serviceTask id="webService" name="Web service invocation" implementation="##WebService" operationRef="tns:prettyPrintCountOperation">

[EXPERIMENTAL]

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'll 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.

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.

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:

Table 7.2. 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
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.

Execution listeners allow you to execute external Java code or evaluate an expression when certain events occur during process exevcution. The events that can be captured are:

The following process definition contains 3 execution listenerss: <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.impl.pvm.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.getevent()); 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}" />

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);

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 values are 'create' (occurs when the task has been created an all task properties are set), 'assignment' (occurs when the task is assigned to somebody) or 'complete' (occurs when the task is completed and just before the task is deleted from the runtime data). 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 ther TaskListener interface, similar to a service task .

<activiti:taskListener event="create" delegateExpression="${myTaskListenerBean}" />

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:

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, ie. 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 (ie. 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.

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 postive 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, foziee] . 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.

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 catched, 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.

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> 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). You can use expressions for the timer duration, by doing so you can influence the timer duration based on process variables. The process variables must contain the ISO 8601 string for the duration. <boundaryEvent id="escalationTimer" cancelActivity="true" attachedToRef="firstLineSupport"> <timerEventDefinition> <timeDuration>${duration}</timeDuration> </timerEventDefinition> </boundaryEvent> In the grafical 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. 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).

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 (eg. concurrent activities, nested subprocesses, etc.). Process execution continues following the outgoing sequence flow of the boundary event.

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 catched: 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).

A subprocess is an activity that contains other activities, gateways, events, etc. which on itself form a process that is part of the bigger process. A subprocess is completely defined inside a parent process (that's why it's often called an embedded subprocess). Subprocesses have two major use cases: Subprocesses allow hierarchical modeling . Many modeling tools allow that subprocesses can be collapsed , hiding all the details of the subprocess and displaying a high-level end-to-end overview of the business process. A subprocess creates a new scope for events . Events that are thrown during execution of the subprocess, can be caught by a boundary event on the boundary of the subprocess, thus creating a scope for that event limited to the subprocess. Using a subprocess does impose some constraints: A subprocess can only have one none start event , no other start event types are allowed. A subprocess must at least have one end event . Note that the BPMN 2.0 specification allows to omit the start and end events in a subprocess, but the current Activiti implementation does not support this. Sequence flow can not cross subprocess boundaries.

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.

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.

Activiti provides a convenient and flexible way to add forms for the manual steps of your business processes. We support two strategies to work with forms: Build-in form rendering and external form rendering

Build-in form rendering is the simplest to get started with. We'll explain it with an example. The demo setup script installs the vacationRequest business process as an example of using task forms through Activiti Explorer. Please check the example for the complete source code. The business process diagram looks like this: To use the build-in rendering, the form files have to be included in the deployment. That can be done programmatically like this:

Deployment deployment = repositoryService.createDeployment()
  .addClasspathResource("org/activiti/examples/taskforms/VacationRequest.bpmn20.xml")
  .addClasspathResource("org/activiti/examples/taskforms/approve.form")
  .addClasspathResource("org/activiti/examples/taskforms/request.form")
  .addClasspathResource("org/activiti/examples/taskforms/adjustRequest.form")
  .deploy();
      Other ways of deploying your processes/forms, can be found in chapter Deployment.
      The BPMN 2.0 specification does not specify how tasks or task forms should be rendered, 
      as such forms are defined using Activiti specific constructs.  
      There is the attribute activiti:formKey that can be specified on 
      startEvents and userTasks.
      

<startEvent id="request" 
            activiti:formKey="org/activiti/examples/taskforms/request.form" />
<sequenceFlow id="flow1" sourceRef="request" targetRef="handleRequest" />
<userTask id="handleRequest" name="Handle vacation request"
          activiti:formKey="org/activiti/examples/taskforms/approve.form" >
  <documentation>
     Vacation request by ${employeeName}
  </documentation> 
 </userTask>

The activiti:formKey attribute can contain any text value which you use to identify your form in case you do your own form rendering. But the build-in form rendering expects the activiti:formKey to be a reference to a resource in the same business archive (= deployment). The resource is identify by it's full path within the deployment. In our case, the form request.form was deployed within a Business-acrhive, in folder org/activiti/examples/taskforms/. Activiti Explorer uses the build-in form rendering engines. Currently, there is only one form rendering engine configured, which is Juel. So it resolves resource files as a Juel expression and the resulting HTML String is sent to the client. In the future, we hope to add a FreeMarker form engine, but that will require more library dependencies so we opt for Juel as the default form engine. Here is the rendered form defined in resource org/activiti/examples/taskforms/request.form. Its a form to capture the data necessary to start a new process instance. And here is the contents of that form file: <h1>Vacation Request</h1> <table> <label> Employee name:<br/> <input type="text" name="employeeName" value="" /> <input type="hidden" name="employeeName_required" value="true" /> <input type="hidden" name="employeeName_type" value="User" /> </label><br/> <label> Number of days:<br/> <input type="number" name="numberOfDays" value="1" min="1" /> <input type="hidden" name="numberOfDays_type" value="Integer" /> </label> <label> First day of vacation: (YYYY-MM-DD)<br/> <input type="date" name="startDate"/> <input type="hidden" name="startDate_type" value="Date"/> </label> <label> Date of return to work: (YYYY-MM-DD)<br/> <input type="date" name="returnDate"/> <input type="hidden" name="returnDate_type" value="Date"/> </label> <label> <input type="checkbox" name="vacationPay"/> Vacation pay requested <input type="hidden" name="vacationPay_boolean" value="true"/> </label> <label> Motivation:<br/> <textarea name="vacationMotivation" value=""></textarea> </label> </table>

<EXPERIMENTAL> The mechanism of using hidden fields to provide extra information such as type and required will be changed in one of the subsequent releases. In the future this type of metadata will be based on FormService.getStartFormData and FormService.getTaskFormData The hidden fields provide extra information to the Activiti Explorer client application. So the Javascript in the browser will use the hidden fields and enhance the corresponding input fields. For example, it's possible to specify that a certain text field is a date and Activiti Explorer will add a date picker. Variable names must be camel-cased. The default type of the process variable that is stored is string. Use a hidden field with the input variable name followed by '_type' to define the type (and hence also the conversion from the HTML input to the variable):

<input type="hidden" name="numberOfDays_type" value="Integer" />

Currently supported are String, Integer, Boolean, Date. Input variables can be made required by adding a hidden field with the input variable name followed by '_required':

<input type="hidden" name="employeeName_required" value="true" />

In Activiti-Explorer, the Date type validates to ISO 8601 (YYYY-MM-DD). This field will also use any native date picker tools in the browser (using the HTML5 input type="date") and fall back on a pop-up date picker using the YUI calendar widget. It is, of course still possible to manually enter your own date, which is validated as you type. The Integer form field in the demo has also been enhanced to make use of HTML5 input type=number, which provides native validation and custom input fields in the supported browser(s), although Activiti-Explorer provides client side validation as well. It is expected that Activiti Explorer in the future will use FormService.getStartFormData instead of these hidden field values to enhance the form input fields. That's why the hidden fields part is marked as experimental. </EXPERIMENTAL>

Use FormService.getRenderedStartForm to get the rendered form string with the API, using the default form engine (JUEL):

String FormService.getRenderedStartForm(String processDefinitionId)

Use FormService.submitStartFormData to start a new process instance with the properties that the user provided as input for the form:

ProcessDefinition FormService.submitStartFormData(String processDefinitionId, Map<String, String> properties)

To learn about the difference between starting a new process instance with this FormService method in comparison with the ProcessInstance RuntimeService.startProcessInstanceById(String processDefinitionId), read the section called “Form properties”

After submitting the form, a process instance is started and now someone of the management team needs to handle the request. The corresponding user task has a task form attached to it, which uses the variables which were given as input by the employee in the start form. These variables are referenced as expressions and are resolved at runtime to their text representation.

<h1>Vacation Approval</h1>
  ${employeeName} would like to take ${numberOfDays} day(s) of vacation.
  Motivation: ${vacationMotivation}
  Do you approve this?
  <select name="vacationApproved">
    <option value="true">Yes</option>
    <option value="false">No</option>
   </select>
   <input type="hidden" name="vacationApproved_type" value="Boolean" />
  <label>
    Motivation:<br/>
    <textarea name="managerMotivation" value=""></textarea>
  </label>
      The manager will now indicate in the form whether the vacation request is approved or not,
      by selecting the appropriate input in the form. 
      The result is stored in as a process variable,
      which is then used in the exclusive gateway after the form is submitted.
<sequenceFlow id="flow5" sourceRef="requestApprovedDecision" targetRef="adjustVacationRequestTask">
  <conditionExpression xsi:type="tFormalExpression">${!vacationApproved}</conditionExpression>
</sequenceFlow>    
      Depending on the input of the manager in the user task, the employee will now
      get a new task in his personal task list, stating that the vacation request was
      disapproved and it needs can be refilled if wanted.
      The employee can now choose to resend the vacation request, which brings process
      execution again in the user task for the manager. Or the employee can throw away
      the request, ending the process.
      

<h1>Adjust vacation Request</h1>
  Your manager has disapproved your vacation request for ${numberOfDays} days. <br/>
  Reason: ${managerMotivation}
  Number of days:<br/>
  <input type="text" name="numberOfDays" value="${numberOfDays}" />
  <input type="hidden" name="numberOfDays_type" value="Integer" />
    

Above we showed the build-in task form rendering. But the API also allows for you to perform your own task form rendering outside of the Activiti Engine. These steps explain the hooks that you can use to render your task forms yourself.

Essentially, all the data that's needed to render a form is assembled in one of these two service methods: StartFormData FormService.getStartFormData(String processDefinitionId) and TaskFormdata FormService.getTaskFormData(String taskId) .

Submitting form properties can be done with ProcessInstance FormService.submitStartFormData(String processDefinitionId, Map<String,String> properties) and void FormService.submitStartFormData(String taskId, Map<String,String> properties)

To learn about how form properties map to process variables, see the section called “Form properties”

You can place any form template resource inside the business archives that you deploy (in case you want to store them versioned with the process). It will be available as a resource in the deployment, which you can retrieve using: String ProcessDefinition.getDeploymentId() and InputStream RepositoryService.getResourceAsStream(String deploymentId, String resourceName); This could be your template definition file, which you can use to render/show the form in your own application.

You can use this capability of accessing the deployment resources beyond task forms for any other purposes as well.

The attribute <userTask activiti:formKey="..." is exposed by the API through String FormService.getStartFormData(String processDefinitionId).getFormKey() and String FormService.getTaskFormData(String taskId).getFormKey() . You could use this to store the full name of the template within your deployment (eg. org/activiti/example/form/my-custom-form.xml ), but this is not required at all. For instance, you could also store a generic key in the form attribute and apply an algorithm or transformation to get to the actual template that needs to be used. This might be handy when you want to render different forms for different UI technologies like e.g. one form for usage in a web app of normal screen size, one form for mobile phone's small screens and maybe even a template for a IM form or an email form.

All information relevant to a business process is either included in the process variables themselves or referenced through the process variables. Activiti supports complex Java objects to be stored as process variables like Serializable objects, JPA entities or whole XML documents as String s.

Starting a process and completing tasks is where people are involved into a process. Communicating with people requires forms to be rendered in some UI technology. In order to facilitate multiple UI technologies easy, the process definition can includes the logic of transforming of the complex Java typed objects in the process variables to a Map<String,String> of properties.

Any UI technology can then build a form on top of those properties. The properties can provide a dedicated (and more limited) view on the process variables. The properties needed to display a form are available in the FormData returnvalues of StartFormData FormService.getStartFormData(String processDefinitionId) and TaskFormdata FormService.getTaskFormData(String taskId) . Those properties are obtained from the process variables.

By default, the build-in form engines, will 'see' the properties as well as the process variables. So there is no need to declare task form properties if they match 1-1 with the process variables. For example, with the following declaration:

<startEvent id="start" />

A form will see all the process variables but the formService.getStartFormData(String processDefinitionId).getFormProperties() will be empty.

In the above case, all the submitted properties will be stored as process variables. This means that by simply adding a new inputfield in the form, a new variable can be stored.

Properties are derived from process variables, but they don't have to be stored as process variables. For example, a process variable could be a JPA entity of class Address. And a form property StreetName used by the UI technology could be linked with an expression #{address.street}

Analogue, the properties that a user is supposed to submit in a form can be stored as a process variable or as a nested property in one of the process variables with a UEL value expression like e.g. #{address.street} .

Analogue the default behavior of properties that are submitted is that they will be stored as process variables unless a formProperty declaration specifies otherwise.

Also type conversions can be applied as part of the processing between form properties and process variables.

For example:

<userTask id="task">
  <extensionElements>
    <activiti:formProperty id="room" />
    <activiti:formProperty id="duration" type="long"/>
    <activiti:formProperty id="speaker" variable="SpeakerName" writable="false" />
    <activiti:formProperty id="street" expression="#{address.street}" required="true" />
  </extensionElements>
</userTask>

It's also possible to provide type meta data as part of the FormData that is returned from methods StartFormData FormService.getStartFormData(String processDefinitionId) and TaskFormdata FormService.getTaskFormData(String taskId)

We support following form property types:

For each form property declared, the following FormProperty information will be made available through List<FormProperty> formService.getStartFormData(String processDefinitionId).getFormProperties() and List<FormProperty> formService.getTaskFormData(String taskId).getFormProperties()

public interface FormProperty {
  /** the key used to submit the property in {@link FormService#submitStartFormData(String, java.util.Map)} 
   * or {@link FormService#submitTaskFormData(String, java.util.Map)} */
  String getId();
  /** the display label */
  String getName();
  /** one of the types defined in this interface like e.g. {@link #TYPE_STRING} */
  FormType getType();
  /** optional value that should be used to display in this property */
  String getValue();
  /** is this property read to be displayed in the form and made accessible with the methods 
   * {@link FormService#getStartFormData(String)} and {@link FormService#getTaskFormData(String)}. */
  boolean isReadable();
  /** is this property expected when a user submits the form? */
  boolean isWritable();
  /** is this property a required input field */
  boolean isRequired();
}

For example:

<startEvent id="start">
  <extensionElements>
    <activiti:formProperty id="speaker" 
      name="Speaker"
      variable="SpeakerName" 
      type="string" />
    <activiti:formProperty id="start" 
      type="date" 
      datePattern="dd-MMM-yyyy" />
    <activiti:formProperty id="direction" type="enum">
      <activiti:value id="left" name="Go Left" />
      <activiti:value id="right" name="Go Right" />
      <activiti:value id="up" name="Go Up" />
      <activiti:value id="down" name="Go Down" />
    </activiti:formProperty>
  </extensionElements>
</startEvent>

All that information is accessible through the API. The type names can be obtained with formProperty.getType().getName() . And even the date pattern is available with formProperty.getType().getInformation("datePattern") and the enumeration values are accessible with formProperty.getType().getInformation("values")

You can use JPA-Entities as process variables, allowing you to:

  • Updating existing JPA-entities based on process variables, that can be filled in on a form in a userTask or generated in a serviceTask.

  • Reusing existing domain model without having to write explicit services to fetch the entities and update the values

  • Make decisions (gateways) based on properties of existing entities.

  • ...

To be able to use JPA-entities, the engine must have a reference to an EntityManagerFactory . This can be done by configuring a reference or by supplying a persistence-unit name. JPA-entities used as variables will be detected automatically and will be handled accordingly.

The example configuration below uses the jpaPersistenceUnitName: <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration"> <!-- Database configurations --> <property name="databaseSchemaUpdate" value="true" /> <property name="jdbcUrl" value="jdbc:h2:mem:JpaVariableTest;DB_CLOSE_DELAY=1000" /> <property name="jpaPersistenceUnitName" value="activiti-jpa-pu" /> <property name="jpaHandleTransaction" value="true" /> <property name="jpaCloseEntityManager" value="true" /> <!-- job executor configurations --> <property name="jobExecutorActivate" value="false" /> <!-- mail server configurations --> <property name="mailServerPort" value="5025" /> </bean> The next example configuration below provides a EntityManagerFactory which we define ourselves (in this case, an open-jpa entity manager). Note that the snippet only contains the beans that are relevant for the example, the others are ommited. Full working example with open-jpa entitymanager can be found in the activiti-spring-examples ( /activiti-spring/src/test/java/org/activiti/spring/test/jpa/JPASpringTest.java ) <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitManager" ref="pum"/> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter"> <property name="databasePlatform" value="org.apache.openjpa.jdbc.sql.H2Dictionary" /> </bean> </property> </bean> <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> <property name="dataSource" ref="dataSource" /> <property name="transactionManager" ref="transactionManager" /> <property name="databaseSchemaUpdate" value="true" /> <property name="jpaEntityManagerFactory" ref="entityManagerFactory" /> <property name="jpaHandleTransaction" value="true" /> <property name="jpaCloseEntityManager" value="true" /> <property name="jobExecutorActivate" value="false" /> </bean> The same configurations can also be done when building an engine programmatically, example: ProcessEngine processEngine = ProcessEngineConfiguration .createProcessEngineConfigurationFromResourceDefault() .setJpaPersistenceUnitName("activiti-pu") .buildProcessEngine(); Configuration properties: jpaPersistenceUnitName: The name of the persistence-unit to use. (Make sure the persistence-unit is available on the classpath. According to the spec, the default location is /META-INF/persistence.xml ). Use either jpaEntityManagerFactory or jpaPersistenceUnitName . jpaEntityManagerFactory: An reference to a bean implementing javax.persistence.EntityManagerFactory that will be used to load the Entities and flushing the updates. Use either jpaEntityManagerFactory or jpaPersistenceUnitName . jpaHandleTransaction: Flag indicating that the engine should begin and commit/rollback the transaction on the used EntityManager instances. Set to false when Java Transaction API (JTA) is used. jpaCloseEntityManager: Flag indicating that the engine should close the EntityManager instance that was obtained from the EntityManagerFactory . Set to false when the EntityManager is container-managed (e.g. when using an Extended Persistence Context which isn't scoped to a single transaction').

Examples for using JPA variables can be found in JPAVariableTest . We'll explain JPAVariableTest.testUpdateJPAEntityValues step by step. First of all, we create a EntityManagerFactory for our persistence-unit, which is based on META-INF/persistence.xml . This contains classes which should be included in the persistence unit and some vendor-specific configuration. We are using a simple entity in the test, having an id and String value property, which is also persisted. Before running the test, we create an entity and save this. @Entity(name = "JPA_ENTITY_FIELD") public class FieldAccessJPAEntity { @Column(name = "ID_") private Long id; private String value; public FieldAccessJPAEntity() { // Empty constructor needed for JPA public Long getId() { return id; public void setId(Long id) { this.id = id; public String getValue() { return value; public void setValue(String value) { this.value = value; We start a new process instance, adding the entity as a variable. As with other variables, they are stored in the persistent storage of the engine. When the variable is requested the next time, it will be loaded from the EntityManager based on the class and Id stored. Map<String, Object> variables = new HashMap<String, Object>(); variables.put("entityToUpdate", entityToUpdate); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("UpdateJPAValuesProcess", variables); The first node in our process definition contains a serviceTask that will invoke the method setValue on entityToUpdate , which resolves to the JPA variable we set earlier when starting the process instance and will be loaded from the EntityManager associated with the current engine's context'. <serviceTask id='theTask' name='updateJPAEntityTask' activiti:expression="${entityToUpdate.setValue('updatedValue')}" /> When the service-task is finished, the process instance waits in a userTask defined in the process definition, which allows us to inspect the process instance. At this point, the EntityManager has been flushed and the changes to the entity have been pushed to the database. When we get the value of the variable entityToUpdate , it's loaded again and we get the entity with it's value property set to updatedValue . // Servicetask in process 'UpdateJPAValuesProcess' should have set value on entityToUpdate. Object updatedEntity = runtimeService.getVariable(processInstance.getId(), "entityToUpdate"); assertTrue(updatedEntity instanceof FieldAccessJPAEntity); assertEquals("updatedValue", ((FieldAccessJPAEntity)updatedEntity).getValue());

A more advanced example, JPASpringTest , can be found in activiti-spring-examples . It describes the following simple use case:

History is the component that captures what happened during process execution and stores it permanently. In contrast to the runtime data, the history data will remain present in the DB also after process instances have completed.

There are 4 history entities:

  • HistoricProcessInstance s containing information about current and past process instances.

  • HistoricActivityInstance s containing information about a single execution of an activity.

  • HistoricTaskInstance s containing information about current and past (completed and deleted) task instances.

  • HistoricDetail s containing various kinds of information related to either a historic process instances, an activity instance or a task instance.

Since the DB contains historic entities for past as well as ongoing instances, you might want to consider querying these tables in order to minimize access to the runtime process instance data and that way keeping the runtime execution performant.

Later on, this information will be exposed in Activiti Explorer and Activiti Probe. Also, it will be the information from which the reports will be generated.

In the API, it's possible to query all 4 of the History entities. The HistoryService exposes the the methods createHistoricProcessInstanceQuery() , createHistoricActivityInstanceQuery() , createHistoricDetailQuery() and createHistoricTaskInstanceQuery() .

Below are a couple of examples that show some of the possibilities of the query API for history. Full description of the possibilities can be found in the the javadocs , in the org.activiti.engine.history package.

The next example, gets all variable-updates that have been done in process with id 123. Only HistoricVariableUpdate s will be returned by this query. Note that it's possible that a certain variable name has multiple HistoricVariableUpdate entries, for each time the variable was updated in the process. You can use orderByTime (the time the variable update was done) or orderByVariableRevision (revision of runtime variable at the time of updating) to find out in what order they occured. historyService.createHistoricDetailQuery() .variableUpdates() .processInstanceId("123") .orderByVariableName().asc() .list()

This example gets all form-properties that were submitted in any task or when starting the process with id "123". Only HistoricFormProperties s will be returned by this query. historyService.createHistoricDetailQuery() .formProperties() .processInstanceId("123") .orderByVariableName().asc() .list()

The last example gets all variable updates that were performed on the task with id "123". This returns all HistoricVariableUpdates for variables that were set on the task (task local variables), and NOT on the process instance.

historyService.createHistoricDetailQuery()
  .variableUpdates()
  .taskId("123")
  .orderByVariableName().asc()
  .list()
  Task local variables can be set using the TaskService or on a DelegateTask, inside TaskListener:
taskService.setVariableLocal("123", "myVariable", "Variable value");

public void notify(DelegateTask delegateTask) { delegateTask.setVariableLocal("myVariable", "Variable value");

When configuring at least audit level for configuration. Then all properties submitted through methods FormService.submitStartFormData(String processDefinitionId, Map<String, String> properties) and FormService.submitTaskFormData(String taskId, Map<String, String> properties) are recorded.

[KNOWN LIMITATION] Currently the forms as worked out in Activiti Explorer do not yet use the submitStartFormData and submitTaskFormData . So the form properties are not yet archived when using the forms in Activity Explorer. A workaround is to set the historyLevel to full and use the variableUpdates to see what values were set in userTasks. @see ACT-294

Form properties can be retrieved with the query API like this:

historyService
      .createHistoricDetailQuery()
      .onlyFormProperties()
      .list();

In that case only historic details of type HistoricFormProperty are returned.

If you've set the authenticated user before calling the submit methods with IdentityService.setAuthenticatedUserId(String) then that authenticated user who submitted the form will be accessible in the history as well with HistoricProcessInstance.getStartUserId() for start forms and HistoricActivityInstance.getAssignee() for task forms.

Activiti comes with an Eclipse plugin, the Activiti Eclipse Designer, that can be used to graphically model, test and deploy BPMN 2.0 processes. The Activiti tool stack offers two modeling / design tools with the Activiti Modeler and the Activiti Designer. You can of course use these tools in your own way, but a common way of working is to use the Activiti Modeler for high level modeling. There should be no technical knowledge necessary to model a process definition with the Activiti Modeler. The Activiti Designer can then be used to add the necessary technical details, like Java service tasks, execution listeners etc. With the import functionality of the Activiti Designer this workflow is well supported. Please note: Activiti Designer is still in a "beta" state. This means that we don't yet support version compatibility. Although, with the BPMN 2.0 XML support of the Activiti Designer you should be able to upgrade your process definitions to new versions of the Activiti Designer quite easily. Also note that not all BPMN 2.0 constructs that are supported by the Activiti Engine are supported in the Activiti Designer.

The following installation instructions are verified on Eclipse Classic Helios . Go to Help -> Install New Software . In the following panel, click on Add button and fill in the following fields:

  • Name: Activiti BPMN 2.0 designer

  • Location: http://activiti.org/designer/update/

Make sure the "Contact all updates sites.." checkbox is not checked, because all the necessary plugins can be downloaded from the Activiti update site.

Create Activiti projects and diagrams. A BPMN 2.0 XML file and an image of the process are automatically generated after each save of the Activiti diagram (the automatic generation can be switched of in the Eclipse preferences in the Activiti tab). The Activiti editor is implemented as a multipage editor. This means that on the first page the process diagram is shown and on the second page the BPMN 2.0 XML. Changes to the diagram are exported to the BPMN 2.0 XML file each time the diagram is saved. And the same goes for the BPMN 2.0 XML file. When you edit the BPMN 2.0 XML file the changes are saved in the diagram as well when you save that file. Note that this is only supported for constructs and attributes that can be edited and created in the diagram view. BPMN 2.0 XML files can be imported into the Activiti Designer and a diagram will be created. There are two ways supported to import a BPMN 2.0 XML file. One is to right-click on the Activiti project in the package explorer and choose the Import BPMN 2.0 file option at the bottom of the popup menu. Then you can choose the BPMN 2.0 XML via the file select dialog and the diagram is created. A second option is to import the BPMN 2.0 XML file to the src/main/resources/diagrams folder of your project (the filename should end with .bpmn20.xml). Then when you open the BPMN 2.0 XML file the diagram is created. Note that the Activiti Designer is able to read the BPMN DI information from the BPMN 2.0 XML file, but in this version the diagram is created by parsing and analyzing the BPMN 2.0 XML only, because this gives the best result. This means that import support is available even for BPMN 2.0 XML files without BPMN DI information. For deployment a BAR file and optionally a JAR file is created by the Activiti Designer by right-clicking on an Activiti project in the package explorer and choosing the Create deployment artifacts option at the bottom of the popup menu. For more information about the deployment functionality of the Designer look a the deployment section.

  • Generate a unit test (right click on a BPMN 2.0 XML file in the package explorer and select generate unit test ) A unit test is generated with an Activiti configuration that runs on an embedded H2 database. You can now run the unit test to test your process definition. The BPMN 2.0 XML is opened in a Activiti XML editor which provides content assist. Note that there are 2 main XSDs configured, the BPMN 2.0 spec XSD and the Activiti extensions XSD. These two XSDs are not yet alligned in a good manneer. A basic validation is performed after each save of the Activiti diagram and the errors are mentioned in the Eclipse problem view. The Activiti project is generated as a Maven project. To configure the dependencies you need to run mvn eclipse:eclipse and the Maven dependencies will be configured as expected. Note that for process design Maven dependencies are not needed. This is only needed to run unit tests.

  • [EXPERIMENTAL] You can extend the default functionality offered by Activiti Designer. This section documents which extensions are available, how they can be used and provides some usage examples. Extending Activiti Designer is useful in cases where the default functionality doesn't suit your needs, you require additional capabilities or have domain specific requirements when modelling business processes. Extension of Activiti Designer falls into two distinct categories, extending the palette and extending output formats. Each of these extension ways requires a specific approach and different technical expertise.

    Note

    Extending Activiti Designer requires technical knowledge and more specifically, knowledge of programming in Java. Depending on the type of extension you want to create, you might also need to be familiar with Maven, Eclipse, OSGi, Eclipse extensions and

    You can customize the palette that is offered to users when modelling processes. The palette is the collection of shapes that can be dragged onto the canvas in a process diagram and is displayed to the right hand side of the canvas. As you can see in the default palette, the default shapes are grouped into compartments (these are called "drawers") for Events, Gateways and so on. There are two options built-in to Activiti Designer to customize the drawers and shapes in the palette:

    In order to customize the palette, you create a JAR file that is added to a specific installation of Activiti Designer (more on how to do that later). Such a JAR file is called an extension . By writing classes that are included in your extension, Activiti Designer understands which customizations you wish to make. In order for this to work, your classes should implement certain interfaces. There is an integration library available with those interfaces and base classes to extend which you should add to your project's classpath.

    You can find the code examples listed below in source control with Activiti Designer. Take a look in the examples/money-tasks directory in the projects/designer directory of Activiti's source code.

    Note

    You can setup your project in whichever tool you prefer and build the JAR with your build tool of choice. For the instructions below, a setup is assumed with Eclipse Helios, using Maven (3.x) as build tool, but any setup should enable you to create the same results.

    Download and extract Eclipse (Galileo or Helios should both work) and a recent version (3.x) of Apache Maven . If you use a 2.x vesion of Maven, you will run into problems when building your project, so make sure your version is up to date. We assume you are familiar with using basic features and the Java editor in Eclipse. It's up to you whether your prefer to use Eclipse's features for Maven or run Maven commands from a command prompt.

    Create a new project in Eclipse. This can be a general project type. Create a pom.xml file at the root of the project to contain the Maven project setup. Also create folders for the src/main/java and src/main/resources folders, which are Maven conventions for your Java source files and resources respectively. Open the pom.xml file and add the following lines:

    <project 
      xmlns="http://maven.apache.org/POM/4.0.0" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>org.acme</groupId>
      <artifactId>money-tasks</artifactId>
      <version>1.0.0</version>
      <packaging>jar</packaging>
      <name>Acme Corporation Money Tasks</name>
            

    As you can see, this is just a basic pom.xml file that defines a groupId, artifactId and version for the project. We will create a customization that includes a single custom node for our money business.

    Add the integration library to your project's dependencies by including this dependency in your pom.xml file:

    <dependencies>
      <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-designer-integration</artifactId>
        <version>0.7.0</version> <!-- Current Activiti Designer Version -->
        <scope>compile</scope>
      </dependency>
    </dependencies>
    <repositories>
      <repository>
          <id>Activiti</id>
          <url>http://maven.alfresco.com/nexus/content/repositories/activiti/</url>
       </repository>
    </repositories>

    Finally, in the pom.xml file, add the configuration for the maven-compiler-plugin so the Java source level is at least 1.5 (see snippet below). You will need this in order to use annotations. You can also include instructions for Maven to generate the JAR's MANIFEST.MF file. This is not required, but you can use a specific property in the manifest to provide a name for your extension (this name may be shown at certain places in the designer and is primarily intended for future use if you have several extensions in the designer). If you wish to do so, include the following snippet in pom.xml:

    <build>
      <plugins>
            <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <configuration>
            <source>1.5</source>
            <target>1.5</target>
            <showDeprecation>true</showDeprecation>
            <showWarnings>true</showWarnings>
            <optimize>true</optimize>
          </configuration>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-jar-plugin</artifactId>
          <version>2.3.1</version>
          <configuration>
            <archive>
              <index>true</index>
              <manifest>
                <addClasspath>false</addClasspath>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
              </manifest>
              <manifestEntries>
                <ActivitiDesigner-Extension-Name>Acme Money</ActivitiDesigner-Extension-Name>
              </manifestEntries>
            </archive>
          </configuration>
        </plugin>
      </plugins>
    </build>

    The name for the extension is described by the ActivitiDesigner-Extension-Name property. The only thing left to do now is tell Eclipse to setup the project according to the instructions in pom.xml. So open up a command shell and go to the root folder of your project in the Eclipse workspace. Then execute the following Maven command:

    mvn eclipse:eclipse

    Wait until the build is successful. Refresh the project (use the project's context menu (right-click) and select Refresh). You should now have the src/main/java and src/main/resources folders as source folders in the Eclipse project.

    Note

    You can of course also use the m2eclipse plugin and simply enable Maven dependency management from the context menu (right-click) of the project. Then choose Maven > Update project configuration from the project's context menu. That should setup the source folders as well.

    That's it for the setup. Now you're ready to start creating customizations to Activiti Designer!

    You might be wondering how you can add your extension to Activiti Designer so your customizations are applied. These are the steps to do just that:

    Diagrams you open will now have the shapes from the new extension in their palette (or shapes disabled, depending on the customizations in your extension). If you already had a diagram opened, close and reopen it to see the changes in the palette.

    With your project set up, you can now easily add shapes to the palette. Each shape you wish to add is represented by a class in your JAR. Take note that these classes are not the classes that will be used by the Activiti engine during runtime. In your extension you describe the properties that can be set in Activiti Designer for each shape. From these shapes, your refer to the runtime class that should be used by the engine. This class should implement JavaDelegate as for any ServiceTask in Activiti.

    A shape's class is a simple Java class, to which a number of annotations are added. The class should implement the CustomServiceTask interface, but you shouldn't implement this interface yourself. Extend the AbstractCustomServiceTask base class instead (at the moment you MUST extend this class directly, so no abstract classes in between). In the Javadoc for that class you can find instructions on the defaults it provides and when you should override any of the methods it already implements. Overrides allow you to do things such as providing icons for the palette and in the shape on the canvas (these can be different) and specifying the base shape you want the node to have (activity, event, gateway).

    * @author John Doe * @version 1 * @since 1.0.0 public class AcmeMoneyTask extends AbstractCustomServiceTask {

    You will need to implement the getName() method to determine the name the node will have in the palette. You can also put the nodes in their own drawer and provide an icon. Override the appropriate methods from AbstractCustomServiceTask . If you want to provide an icon, make sure it's in the src/main/resources package in your JAR and is about 16x16 pixels and a JPEG or PNG format. The path you supply is relative to that folder.

    You can add properties to the shape by adding members to the class and annotating them with the @Property annotation like this:

    @Property(type = PropertyType.TEXT, displayName = "Account Number")
    @Help(displayHelpShort = "Provide an account number", displayHelpLong = HELP_ACCOUNT_NUMBER_LONG)
    private String accountNumber;

    There are several PropertyType values you can use, which are described in more detail in this section . You can make a field required by setting the required attribute to true. A message and red background will appear if the user doesn't fill out the field.

    If you want to ensure the order of the various properties in your class as they appear in the property screen, you should specify the order attribute of the @Property annotation.

    As you can see, there's also an @Help annotation that's used to provide the user some guidance when filling out the field. You can also use the @Help annotation on the class itself - this information is shown at the top of the property sheet presented to the user.

    Below is the listing for a further elaboration of the MoneyTask . A comment field has been added and you can see an icon is included for the node.

    * @author John Doe * @version 1 * @since 1.0.0 @Runtime(delegationClass = "org.acme.runtime.AcmeMoneyJavaDelegation") @Help(displayHelpShort = "Creates a new account", displayHelpLong = "Creates a new account using the account number specified") public class AcmeMoneyTask extends AbstractCustomServiceTask { private static final String HELP_ACCOUNT_NUMBER_LONG = "Provide a number that is suitable as an account number."; @Property(type = PropertyType.TEXT, displayName = "Account Number", required = true) @Help(displayHelpShort = "Provide an account number", displayHelpLong = HELP_ACCOUNT_NUMBER_LONG) private String accountNumber; @Property(type = PropertyType.MULTILINE_TEXT, displayName = "Comments") @Help(displayHelpShort = "Provide comments", displayHelpLong = "You can add comments to the node to provide a brief description.") private String comments; * (non-Javadoc) * @see org.activiti.designer.integration.servicetask.AbstractCustomServiceTask #contributeToPaletteDrawer() @Override public String contributeToPaletteDrawer() { return "Acme Corporation"; @Override public String getName() { return "Money node"; * (non-Javadoc) * @see org.activiti.designer.integration.servicetask.AbstractCustomServiceTask #getSmallIconPath() @Override public String getSmallIconPath() { return "icons/coins.png";

    If you extend Activiti Designer with this shape, The palette and corresponding node will look like this:

    The properties screen for the money task is shown below. Note the required message for the accountNumber field.

    The help for fields is offered by the buttons to the right of each property. Clicking on the button shows a popup as displayed below.

    The final step for your shape is to indicate the class that is instantiated by the Activiti engine when it reaches your node when executing a process instance. To do this, you use the @Runtime annotation. The delegationClass attribute you return should contain the canonical name of the runtime class. Note that the runtime class shouldn't be in your extension JAR, as it's dependent on the Activiti libraries.

    @Runtime(delegationClass = "org.acme.runtime.AcmeMoneyJavaDelegation")

    This section describes the property types you can use for a CustomServiceTask by setting its type to a PropertyType value.

    Creates a data grid control as shown below. A data grid can be used to allow the user to enter an arbitrary amount of rows of data and enter values for a fixed set of columns in each of those rows (each individual combination of row and column is referred to as a cell). Rows can be added and removed as the user sees fit.

    This property type expects the class member you have annotated to also have an accompanying @DataGridProperty annotation (for an example, see below). Using this additional annotation, you can specify some specific attributes of the data grid. You are required to reference a different class to determine which columns go into the grid with the itemClass attribute. Activiti Designer expects the member type to be a List . By convention, you can use the class of the itemClass attribute as its generic type. If, for example, you have a grocery list that you edit in the grid, you would define the columns of the grid in the GroceryListItem class. From your CustomServiceTask , you would refer to it like this:

    @Property(type = PropertyType.DATA_GRID, displayName = "Grocery List")
    @DataGridProperty(itemClass = GroceryListItem.class)
    private List<GroceryListItem> groceryList;

    The "itemClass" class uses the same annotations you would otherwise use to specify fields of a CustomServiceTask , with the exception of using a data grid. Specifically, TEXT , MULTILINE_TEXT and PERIOD are currently supported. You'll notice the grid will create single line text controls for each field, regardless of the PropertyType . This is done on purpose to keep the grid graphically appealing and readable. If you consider the regular display mode for a PERIOD PropertyType for instance, you can imagine it would never properly fit in a grid cell without cluttering the screen. For MULTILINE_TEXT and PERIOD , a double-click mechanism is added to each field which pops up a larger editor for the PropertyType . The value is stored to the field after the user clicks OK and is therefore readable within the grid.

    Required attributes are handled in a similar manner to regular fields of type TEXT and the entire grid is validated as soon as any field loses focus. The background color of the text control in a specific cell of the data grid is changed to light red if there are validation failures.

    By default, the component allows the user to add rows, but not to determine the order of those rows. If you wish to allow this, you should set the orderable attribute to true, which enables buttons at the end of each row to move it up or down in the grid.

    This customization requires you to include a class in your extension that implements the DefaultPaletteCustomizer interface. You should not implement this interface directly, but subclass the AbstractDefaultPaletteCustomizer base class. Currently, this class provides no functionality, but future versions of the DefaultPaletteCustomizer interface will offer more capabilities for which this base class will provide some sensible defaults so it's best to subclass so your extension will be compatible with future releases.

    Extending the AbstractDefaultPaletteCustomizer class requires you to implement one method, disablePaletteEntries() , from which you must return a list of PaletteEntry values. For each of the default shapes, you can disable it by adding its corresponding PaletteEntry value to your list. Note that if you remove shapes from the default set and there are no remaining shapes in a particular drawer, that drawer will be removed from the palette in its entirety. If you wish to disable all of the default shapes, you only need to add PaletteEntry.ALL to your result. As an example, the code below disables the Manual task and Script task shapes in the palette.

    public class MyPaletteCustomizer extends AbstractDefaultPaletteCustomizer {
       * (non-Javadoc)
       * @see org.activiti.designer.integration.palette.DefaultPaletteCustomizer#disablePaletteEntries()
      @Override
      public List<PaletteEntry> disablePaletteEntries() {
        List<PaletteEntry> result = new ArrayList<PaletteEntry>();
        result.add(PaletteEntry.MANUAL_TASK);
        result.add(PaletteEntry.SCRIPT_TASK);
        return result;
            

    The result of applying this extension is shown in the picture below. As you can see, the manual task and script task shapes are no longer available in the Tasks drawer.

    To disable all of the default shapes, you could use something similar to the code below.

    public class MyPaletteCustomizer extends AbstractDefaultPaletteCustomizer {
       * (non-Javadoc)
       * @see org.activiti.designer.integration.palette.DefaultPaletteCustomizer#disablePaletteEntries()
      @Override
      public List<PaletteEntry> disablePaletteEntries() {
        List<PaletteEntry> result = new ArrayList<PaletteEntry>();
        result.add(PaletteEntry.ALL);
        return result;
            

    The result will look like this (notice that the drawers the default shapes were in are no longer in the palette):

    Besides customizing the palette, you can also create extensions to Activiti Designer that can perform validations and save information from the diagram to custom resources in the Eclipse workspace. There are built-in extension points for doing this and this section explains how to use them.

    Activiti Designer allows you to write extensions that validate diagrams. There are already validations of BPMN constructs in the tool by default, but you can add your own if you want to validate additional items such as modeling conventions or the values in properties of CustomServiceTask s. These extensions are known as Process Validators .

    You can also Activiti Designer to publish to additional formats when saving diagrams. These extensions are called Export Marshallers and are invoked automatically by Activiti Designer on each save action by the user. This behavior can be enabled or disabled by setting a preference in Eclipse's preferences dialog for each format to be saved.

    You can compare these extensions to the BPMN 2.0 validation, BPMN 2.0 export and process image saving that's performed during save actions by default in Activiti Designer. In fact, these functions use exactly the same extension features you can use to save to your own formats.

    Often, you will want to combine a ProcessValidator and an ExportMarshaller . Let's say you have a number of CustomServiceTask s in use that have properties you would like to use in the process that gets generated. However, before the process is generated, you want to validate some of those values first. Combining a ProcessValidator and ExportMarshaller is the best way to accomplish this and Activiti Designer enables you to plug your extensions into the tool seamlessly.

    To create a ProcessValidator or an ExportMarshaller , you need to create a different kind of extension than for extending the palette. The reason for this is simple: from your code you will need access to more APIs than are offered by the integration library. In particular, you will need classes that are available in Eclipse itself. So to get started, you should create an Eclipse plugin (which you can do by using Eclipse's PDE support) and package it in a custom Eclipse product or feature. It's beyond the scope of this user guide to explain all the details involved in developing Eclipse plugins, so the instructions below are limited to the functionality for extending Activiti Designer.

    Your bundle should be dependent on the following libraries:

    Both ProcessValidator s and ExportMarshaller s are created by extending a base class. These base classes inherit some useful methods fromt heir superclass, the AbstractDiagramWorker class. Using these methods you can create information, warning and error markers that show up in Eclipse's problems view for the user to figure out what's wrong or important. You can also access the diagram through Resources and InputStreams for the diagram's content using these methods in the AbstractDiagramWorker class.

    It's probably a good idea to invoke clearMarkers() as one of the first things you do in either a ProcessValidator or an ExportMarshaller ; this will clear any previous markers for your worker (markers are automatically linked to the worker and clearing markers for one worker leaves other markers untouched). For example:

    // Clear markers for this diagram first
    clearMarkers(getResource(diagram.eResource().getURI()));

    You should also use the progress monitor provided to report your progress back to the user because validations and/or marshalling actions can take up some time during which the user is forced to wait. Reporting progress requires some knowledge of how you should use Eclipse's features. Take a look at this article for a thorough explanation of the concepts and usage.

    Creating a ProcessValidator extension

    Create an extension to the org.activiti.designer.eclipse.extension.validation.ProcessValidator extension point in your plugin.xml file. For this extension point, you are required to subclass the AbstractProcessValidator class.

    <?eclipse version="3.6"?>
    <plugin>
      <extension
        point="org.activiti.designer.eclipse.extension.validation.ProcessValidator">
        <ProcessValidator
          class="org.acme.validation.AcmeProcessValidator">
        </ProcessValidator>
      </extension>
    </plugin>

    public class AcmeProcessValidator extends AbstractProcessValidator {
            

    You have to implement a number of methods. Most importantly, implement getValidatorId() so you return a globally unique ID for your validator. This will enable you to invoke it from and ExportMarshaller, or event let someone else invoke your validator from their ExportMarshaller. Implement getValidatorName() and return a logical name for your validator. This name is shown to the user in dialogs. In getFormatName(), you can return the type of diagram the validator typically validates.

    The validation work itself is done in the validateDiagram() method. From this point on, it's up to your specific functionality what you code here. Typically, however, you will want to start by getting hold of the nodes in the diagram's process, so you can iterate through them, collect, compare and validate data. This snippet shows you how to do this:

    final EList<EObject> contents = getResourceForDiagram(diagram).getContents();
    for (final EObject object : contents) {
      if (object instanceof StartEvent ) {
      // Perform some validations for StartEvents
      // Other node types and validations
            

    Don't forget to invoke addProblemToDiagram() and/or addWarningToDiagram(), etc as you go through your validations. Make sure you return a correct boolean result at the end to indicate whether you consider the validation as succeeded or failed. This can be used by and invoking ExportMarshaller to determine the next course of action.

    Create an extension to the org.activiti.designer.eclipse.extension.export.ExportMarshaller extension point in your plugin.xml file. For this extension point, you are required to subclass the AbstractExportMarshaller class. This abstract base class provides you with a number of useful methods when marshalling to your own format, but most importantly it allows you to save resources to the workspace and to invoke validators.

    <?eclipse version="3.6"?>
    <plugin>
      <extension
        point="org.activiti.designer.eclipse.extension.export.ExportMarshaller">
        <ExportMarshaller
          class="org.acme.export.AcmeExportMarshaller">
        </ExportMarshaller>
      </extension>
      </plugin>

    public class AcmeExportMarshaller extends AbstractExportMarshaller {
            

    You are required to implement some methods, such as getMarshallerName() and getFormatName(). These methods are used to display options to the user and to show information in progress dialogs, so make sure the descriptions you return reflect the functionality you are implementing.

    The bulk of your work is performed in the marshallDiagram(Diagram diagram, IProgressMonitor monitor) method. You are provided with the diagram object, which contains all of the information about the objects in the diagram (BPMN constructs) and the graphical representation.

    If you want to perform a certain validation first, you can invoke the validator directly from your marshaller. You receive a boolean result from the validator, so you know whether validation succeeded. In most cases you won't want to proceed with marshalling the diagram if it's not valid, but you might choose to go ahead anyway or even create a different resource if validation fails. For example:

    final boolean validDiagram = invokeValidator(AcmeConstants.ACME_VALIDATOR_ID, diagram, monitor);
    if (!validDiagram) { 
      addProblemToDiagram(diagram, "Marshalling to " + getFormatName() + " format was skipped because validation of the diagram failed.", null);
    } else {
      //proceed with marshalling
            

    As you can see, here we have chosen to cancel the marshalling if the validator (identified by a constant here) returns false as result. We have also added an additional marker to the diagram so the user can see an explanation why the file wasn't created. This is not required, but seems helpful to the user and shows how you can use these utilities from both ProcessValidators and ExportMarshallers.

    Once you have all the data you need, you should invoke the saveResource() method to create a file containing your data. You can invoke saveResource() as many times as you wish from a single ExportMarshaller; a marshaller can therefore be used to create more than one output file.

    You can construct a filename for your output resource(s) by using some of the methods in the AbstractDiagramWorker class. There are a couple of useful variables you can have parsed, allowing you to create filenames such as <original-filename>_<my-format-name>.xml. These variables are described in the Javadocs, but here's an example how to use one of them:

    private static final String FILENAME_PATTERN = ExportMarshaller.PLACEHOLDER_ORIGINAL_FILENAME + ".acme.axml";
    saveResource(getRelativeURIForDiagram(diagram, FILENAME_PATTERN), bais, this.monitor);

    What happens here is that a static member is used to describe the filename pattern (this is just a best practice, you can specify the string any way you like of course) and the pattern uses the ExportMarshaller.PLACEHOLDER_ORIGINAL_FILENAME constant to insert a variable for the original filename. Later on in the marshallDiagram() method, getRelativeURIForDiagram() is invoked and it will parse the filename for any variables and substitute them. You provide saveResource() with an InputStream to your data and it will save the data to a resource with a relative path to the original diagram.

    Again, you should also use the progress monitor provided to report your progress back to the user. How to do this is described in this article.

    The Activiti Modeler is a web based process editor that can be used to author BPMN 2.0 process graphically using nothing more then a browser. The process files are stored by the server on a the file system, such that they are easily accessible and can be imported without hassles into any Java IDE.

    Activiti Modeler is a separate component developed by Signavio and hosted in a google code project . The license for the Activiti Modeler is MIT Bugs and issues can be reported on the Signavio core components issue tracker . The Activiti Modeler is automatically installed during the demo setup .

    Activiti Cycle is a web application that provides a collaborative platform for different stakeholders of BPM projects (Business, developers, IT operations, ...). It combines different repositories like the Activiti Modeler repository, Subversion or your local file system into a single view, which makes it easy to browse all the artifacts that are involved in the process of creating an executable business process. In addition to this, Activiti Cycle provides built in actions like moving artifacts between repositories or downloading different formats of process models, and a plug-in infrastructure for custom actions. Below is a screen shot of Cycle in a default configuration as pre-configured in the demo setup. Please note: Activiti Cycle is still in a "beta" state. With collaborative tools like Activiti Cycle we dont have years of experience yet, like we have with the core engine. Currently most features are already stable, but with adding more use cases and features, we still have to do some tuning of the APIs or database tables. This means, that for example the Plugin-API is still subject to change, which you have to know when you start writing own Plug-Ins. Or the database might change without having out-of-the-box database migration scripts ready. But be sure: We work hard on removing the beta status, meaning you will get a stable API and database schema.

    The above image shows the Activiti Cycle repository tree. By default it provides access to the repository of the Activiti Modeler and a demo Eclipse workspace, which is located at ${activiti.home}/workspace/activiti-cycle-examples in your local file system. The repositories are plugged in as repository connectors. You'll find the configuration in the table CYCLE_CONN_CONFIG in the Activiti database, which you can access as described in the section called “Inspecting the database” . The database schema has 6 columns:

    1. PLUGIN_ID_ : The plugin-id of the connector (e.g. "org.activiti.cycle.impl.connector.fs.FileSystemConnector" for the filesystem connector and "org.activiti.cycle.impl.connector.signavio.SignavioConnector" for the connector to the activiti modeler.)
    2. INSTANCE_NAME_ : Name of the connector instance. This is the string which is rendered as root folder for this connector in the cycle repository tree. Note that it is possible to use the same connector multiple times, to connect to different repositories of the same type (e.g. to multiple eclipse workspaces using the filesystem connector). In that case the CYCLE_CONN_CONFIG will contain multiple lines with the same PULGIN_ID_ but different INSTANCE_NAME_ s and different INSTANCE_ID_ s (see below).
    3. INSTANCE_ID_ : is used internally to address a connector instance.
    4. USER_ : the user the current connector instance is configured for. (NOTE: This is not the login-name for the connector, but the Activiti Cycle username, e.g. "kermit" in the demo-configuration.)
    5. GROUP_ : a connector instance can be either configured for a single user, or for a group. If the "GROUP_" field is set for a given connector instance, cycle will load this instance for every user belonging to that group.
    6. VALUES_ : an XML string containing the values for the current configuration. It is a serialized map of the form <map>(<entry><string>KEY</string><string>VALUE</string></entry>)*</map>

    Executing the following SQL statement sets the default configuration for "kermit", including an instance of the filesystem connector and an instance of the signavio connector used to access the activiti modeler repository: insert into ACT_CY_CONN_CONFIG values ('1', 'org.activiti.cycle.impl.connector.fs.FileSystemConnector', 'Eclipse Workspace (File System)', 'Workspace', 'kermit', '', '<map><entry><string>basePath</string><string>/path-to-activiti-home/activiti-5.0.beta2/workspace/activiti-cycle-examples</string></entry></map>'); insert into ACT_CY_CONN_CONFIG values ('2', 'org.activiti.cycle.impl.connector.signavio.SignavioConnector', 'Activiti Modeler', 'Activiti', 'kermit', '', '<map> <entry><string>signavioBaseUrl</string><string>http://localhost:8080/activiti-modeler/</string></entry> <entry><string>loginRequired</string><boolean>false</boolean></entry> </map>'); Now, if you want to add, e.g., a Signavio Process Modeler repository, you can add a configuration for the PLUGIN_ID_ 'org.activiti.cycle.impl.connector.signavio.SignavioConnector'. If you need a Signavio account, you can register for a free trial at the Signavio web site . Here is an example configuration: <entry><string>signavioBaseUrl</string><string>https://editor.signavio.com/</string></entry> <entry><string>loginRequired</string><boolean>true</boolean></entry> <entry><string>username</string><string>your user name</string></entry> <entry><string>password</string><string>secret</string></entry> You may also choose not to save your username and password in the database and leaving 'username' and 'password' empty. Cycle will then ask you for your credentials when it needs them. Another thing you might consider is to add a collection of BPMN examples from the Oryx Project . In order to do this, you need a connector configuration for the PLUGIN_ID_ 'org.activiti.cycle.impl.connector.signavio.SignavioConnector': <entry><string>type</string><string>oryx</string></entry> <entry><string>signavioBaseUrl</string><string>http://oryx-project.org/</string></entry> <entry><string>loginRequired</string><boolean>false</boolean></entry>

    <EXPERIMENTAL> ... the whole REST interface is still experimental

    Activiti includes a REST API to the engine that will be deployed to your server when you run the setup script. The REST API uses JSON format (http://www.json.org) and is built upon the Spring Webscripts (http://www.springsurf.org). It is possible to browse the REST API if you point your browser to http://localhost:8080/activiti-rest/service/index and login as one of the admin users (kermit). If you click the "Browse by Web Script Package" link you will get an overview of the packages that are deployed on the server and you can easily navigate into a package to see the REST API calls that are defined for that package. The "Alfresco JavaScript Debugger" can be ignored since Java is used instead of JavaScript to implement the logic. Each REST API call has its individual authorization level and you must be logged in as a user to invoke a REST API call (except for the /login service). Authentication is done using Basic HTTP Authentication, so if you logged in as an admin (i.e. kermit) to browse the REST API, as described above, you should be able to perform all calls as described below. The API follows normal REST API conventions using GET for read operations, POST for creating objects, PUT for updating and performing operations on already created objects and finally DELETE for deleting objects. When performing a call that affects multiple objects POST is used on all such operations for consistency and making sure that an unlimited number of objects may be used. The reason for using POST is that the HTTP DELETE method doesn't implicitly allow request bodies and therefore, a call using DELETE, in theory, could get it's request body stripped out by a proxy. So to be certain this doesn't happen we use POST, even when PUT could have been used to update multiple objects, for consistency. All rest calls use a content type of "application/json" (except for upload requests which uses "multipart/form-data"). The base URL for invoking a REST call is http://localhost:8080/activiti-rest/service/. So for example to list the process definitions in the engine point your browser to: http://localhost:8080/activiti-rest/service/process-engine Please look below to see what REST API calls that currently are available. Please consider the "API" sections as a "one line hint" to what functionality of the core API that is used to implement the REST API call.

    Start Process Instance

    Creates a process instance based on a process definition and returns details about the newly created process instance. Additional variables (from a form) may be passed using the body object. In other words placing attributes next to the "processDefinitionId" attribute. These additional variables may also be described using "meta data fields". A variable named "numberOfDays" with value "2" may be described as an int using an extra variable named "numberOfDays_type" set to "Integer" and to describe it as a required variable use an extra variable named "numberOfDays_required" set to "true". If no type descriptor is used the value will be treated as a String as long as its surrounded by '"'-characters. Its also possible to set the type to "Boolean". Note that if a value is submitted as true (instead of "true") it will be treated as a Boolean even if no descriptor is used. The same is also valid for number, i.e., 123 will become an Integer but "123" will become a String (unless a descriptor is defined). Note that no variables containing "_" in the name will be saved, they are only treated as meta data fields. The reason for using these meta data fields is to make it possible using a standard HTML form to submit the values (since an HTML form submits everything as strings its not possible to distinguish the type of a value as in JSON). HTML submission will be supported in the near future. It is of course not an optimal solution to let the client send instructions to the server about which variables that are required and what type they have, but this is a temporary solution to enable simple form handling. We are currently looking for more proper solutions for forms, containing real meta models that can be used on the server to avoid using meta data fields like above. Please feel free to give suggestions or tips in the Activiti Forum. Request: POST /process-instance "processDefinitionId":"financialReport:1:1700", "businessKey":"order-4711" ProcessEngines.getProcessEngine(configuredProcessEngineName).getProcessService().startProcessInstanceById(processDefinitionId[, businessKey][, variables])

  • Response: "id": "217", "processDefinitionId": "financialReport:1:1700", "activityNames": ["writeReportTask"], "ended": true Request providing processDefinitionKey instead of Id: POST /process-instance "processDefinitionKey":"financialReport:1", "businessKey":"order-4711"

  • The whole REST interface is still experimental ... </EXPERIMENTAL>

    Chapter 15. Activiti Explorer

    Activiti KickStart is a webbased tool to quickly create 'adhoc' business processes using a subset of constructs available to the Activiti engine. KickStart provides a simple UI that doesn't require to learn BPMN or any modeling environment, as it works with concepts that are familiar to every business user. However, the processes that are created using KickStart, are fully BPMN 2.0 compliant and can be used as a starting point for more complex BPM endeavours. KickStart integrates perfectly with the Activiti engine. As such, processes created with KickStart are immediataly usable in Activiti Explorer and are visible in Probe. KickStart serves many business cases, but the following three are probably the most common: Simple business processes: some processes are just simple by nature, and every company has them. Think about an expense process, a holiday leave process, a hiring process, etc... These kind of processes are probably already being done using paper or e-mail. KickStart allows to model these processes quickly and change them whenever it is needed. As such, KickStart really lowers the threshold to automate these business processes. Prototyping: before diving into complex BPMN 2.0 modeling and thinking about the corner cases of the process, it is often wise to get all people involved aligned and work out a prototype that shows the vision of what needs to be done. KickStart allows to do exatcly that: create a business process prototype on the fly, to get your ideas visible for everyone. Adhoc work: in some cases, coordination is required between different people or groups in a company. You know how it normally goes: sending an email here, doing a telephone there ... which often ends up in a tarpit of nobody knowing what or when something needs to be done. However, a business process management platform such as Activiti is an excellent way of distributing and follow-up everything, as it is intended to track exactly such things. KickStart allows you to create processes for adhoc work in a matter of minutes, and distribute and coordinate tasks between people easily. Following screenshots show the capabilities of Activiti KickStart. Take for example the next picture. It shows how an expense process is created in a matter of a few minutes. After clicking the save button, the process is immediately usable in Activiti Explorer: KickStart also allows to define forms for every task: Which are obviously directly usable in Activiti Explorer: At any time during process creation, it is possible to view the corresponding BPMN 2.0 diagram of the business process: Whenever it is required, the processes defined with KickStart can be openend up and modified: Processes created with KickStart are fully compliant with the BPMN 2.0 XML, which means that the process can be imported into any BPMN 2.0 editor:

    Activiti KickStart is currently not yet using the REST API, but uses the service API by including the Activiti engine as a library. While Explorer, Probe and Cycle use the same mechanism for changing the database (see here ), the same does not yet apply for KickStart. To change the database that KickStart uses in the demo setup, generate a new activiti.cfg.jar, and place it in the apps/apache-tomcat-6.x/webapps/activiti-kickstart/WEB-INF/lib folder. Likewise, you must also place your database driver in the same folder. You can find the driver for your database (except Oracle) in setup/files/dependencies/libs/ .

    The jBPM migration is considered [EXPERIMENTAL] . It is possible to migrate an existing installation of jBPM version 3 to Activiti. The migration includes both process definition conversion (ie. from JPDL 3 to BPMN 2.0) and data migration. The migration tool is currently offered as a 'preview' only! The migration coverage is at this point in time not sufficient to be usable on production process definitions and databases. Also note that the migration is a 'best effort' , which means that you may need to extend the migration logic to cover all your use cases. The migration tool is separately availbale as a zip file from the activiti.org download site . Once you have unzipped this file, you will see following files and folders: build.xml : This ant buildfile contains the targets to execute the process definition conversion and database migration. activiti.db.properties and jbpm3.db.properties : these properties files contain the database connection parameters for the jBPM 3 and Activiti database. These properties files must be changed when executing a database migration . processes : when running a process definition conversion , the jBPM 3 process definition files must be placed in this folder. src : this folder contains the source code of the migration tool. Read the extension section if you want to tailor or extend the migration code to your needs. lib : this folder contains all jars required to execute the migration logic.

    It is possible to only convert the process definitions xml from JPDL to BPMN 2.0 that can be executed on the Activiti engine. To do this, place the jBPM 3 processes inside the processes folder. There can be any number of (nested) subfolders, the tool will scan every (sub)folder inside processes to discover processdefinition.xml files (file name must be processdefinition.xml !). The discovered processes will be parsed and deployed to an in-memory databse, such that the logic of reverse engineering the process definition from the jBPM deployment tables contained in the database migration is used for both cases. Once the jBPM 3 processes are placed in the processes folder, execute the convert.processes target in the root of the unzipped migration tool folder:

    ant convert.processes

    During conversion, you will see plenty logging passing by describing where and how the process conversion is being executed. At the end of the conversion, you will see following logging appear: As shown in the logging, the resulting BPMN 2.0 processes can be find in the converted-process-xxxx folder, where the xxxx is the timestamp of the conversion. In this release, only limited support for start, end, wait state and task-nodes is implemented. In the future, this coverage will expand.

    The migration logic is written in such a way, that it is easy to extend to suit your needs. The source code is available as a Maven 2 project in the src subfolder of the downloaded zip file. To build a new zip file, after having changed or extended the logic, simply execute a

    mvn clean install

    to produce a new zip file in the target folder. Following picture gives a high-level overview of the classes in the migration logic. Both the ProcessConversion and ProcessDataMigration classes have a main method that directly is called from the ant build script in the root of the migration zip. These classes construct a ServiceFactory based on the two properties files, using a static factory method

    ServiceFactory.configureFromProperties(jbpmDbProperties, activitiDbProperties);

    The services are constructed by the ServiceFactory (eg. getProcessConversionService() ) and are used to execute the migration logic: public void execute() throws IOException { // convert processes ServiceFactory serviceFactory = createServiceFactory(); ProcessConversionService processConversionService = serviceFactory.getProcessConversionService(); Map<String, Document> migratedProcesses = processConversionService.convertAllProcessDefinitions(); // write results to bpmn20.xml files writeConvertedProcesses(migratedProcesses, workingDir); // Deploy processes to Activiti ActivitiService activitiService = serviceFactory.getActivitiService(); activitiService.deployConvertedProcesses(migratedProcesses); // data migration The ProcessConversionService is an interface that contains process conversion and process definition data retrievel operations. It uses an implementation of Jbpm3Dao . The default implementation of this class uses a Hibernate SessionFactory to retrieve all the data from jBPM 3 tables. The ActivitiService offers operation needed to get the migrated data in the Activiti tables. For example, deploying the converted process definitions is such an operation All these dependencies, ProcessConversionService, Jbpm3Dao, Sessionfactory, ActivitiService and ProcessEngine , are interfaces and can be implemented by your own implementation. You can inject them into the ServiceFactory using regular JavaBean setters. When no such custom implementation is set, the ServiceFactory will fall back to creating the default implementation: public ProcessConversionService getProcessConversionService() { if (processConversionService == null) { this.processConversionService = createDefaultProcessConversionService(); return processConversionService; protected ProcessConversionService createDefaultProcessConversionService() {