3、配置

3. 配置

3.1. 创建流程引擎

Activiti 的流程是通过activiti.cfg.xml文件来配置的。 这个配置文件不适用集成String的方式。 the Spring style of building a process engine.

获取 ProcessEngine, 最简单的方法是用 org.activiti.engine.ProcessEngines 类:

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine() 
这个操作会查找类路径中的  activiti.cfg.xml文件并基于配置文件构建一个引擎。以下是示例配置的代码片段。详细的配置属性会在后边的章节中说明。
<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="asyncExecutorActivate" value="false" />

    <property name="mailServerHost" value="mail.my-corp.com" />
    <property name="mailServerPort" value="5025" />
  </bean>

</beans>

这个xml实际就是一个 Spring 配置文件。当然,并不说 Activiti只能跑在 Spring 环境哦! 我们只是为了方便,所以用了Spring的解析和依赖注入功能来创建引擎。

可以指定配置文件以编程的方式创建ProcessEngineConfiguration 对象,也可能使用不同的 bean id (等,见第 3行).

1 2 3 4 5
ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault(); ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource); ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource, String beanName); ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(InputStream inputStream); ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(InputStream inputStream, String beanName);

也可以不用配置文件,那所有的就配置项都使用默认值 (参考the different supported classes for more information).

1 2
ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration(); ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();

所有这些ProcessEngineConfiguration.createXXX()方法返回一个 ProcessEngineConfiguration以备后用。然后调用 buildProcessEngine() 操作, ProcessEngine 就建好了。

1 2 3 4 5
ProcessEngine processEngine = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration() .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE) .setJdbcUrl("jdbc:h2:mem:my-own-db;DB_CLOSE_DELAY=1000") .setAsyncExecutorActivate(false) .buildProcessEngine();

3.2. ProcessEngineConfiguration bean

 activiti.cfg.xml 必须包含一个ID为 'processEngineConfiguration'.的Bean。

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

这个豆子用于构建ProcessEngine。有多类可用于定义 processEngineConfiguration。这些类代表了不同的运行环境,所以都设置了默认值。最好的做法就是选择与生产环境相匹配的类,将引擎配置相关的属性数据降到最少。以下当前版本可用的类 (大部分会保留到将来版本中):

  • org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration: 独立运行方式。活动中要注意事务处理。默认情况下,只在引擎启动的时候检查数据库(如果没有找到Activiti 的表结构或版本不对就会抛异常)。

  • org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration: 用于单元测试的辅助类。Activiti会处理好事务。缺省使用H2 内存数据库。数据在引擎启动时创建关闭时删除。用这个就基本上不用其它的配置了 (除非要用到工作执行器或邮件功能)。

  • org.activiti.spring.SpringProcessEngineConfiguration: 用于 Spring 环境。参考 the Spring integration section 

  • org.activiti.engine.impl.cfg.JtaProcessEngineConfiguration: 单独运行模式集合 JTA .

3.3. 数据库配置

 Activiti engine使用的数据库有两种配置方式。第一个选项是定义JDBC 数据库属性:

  • jdbcUrl: JDBC 数据库连接字符串。

  • jdbcDriver: 具体实现的驱动类。

  • jdbcUsername: 数据库账号。

  • jdbcPassword:数据库密码。

JDBC数据源包含 MyBatis 连接池设置。以下可项用于调整连接池(出版 MyBatis 文档):

  • jdbcMaxActiveConnections: 连接池中的最大连接数量。默认是10。

  • jdbcMaxIdleConnections: 连接池中最大空闲连接数量。

  • jdbcMaxCheckoutTime: 强行取出连接池中连接的等待时间(毫秒),默认是20000 (20 秒).

  • jdbcMaxWaitTime: 最大连接超时时间,默认20000 (20 秒).

数据库配置示例:

1 2 3 4
<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="" />

Our benchmarks have shown that the MyBatis connection pool is not the most efficient or resilient when dealing with a lot of concurrent requests. As such, it is advised to us a javax.sql.DataSource implementation and inject it into the process engine configuration (For example DBCP, C3P0, Hikari, Tomcat Connection Pool, etc.):

1 2 3 4 5 6 7 8 9 10 11 12
<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 a data source. So you have to make sure that the libraries are on your classpath.

The following properties can be set, regardless of whether you are using the JDBC or data source approach:

  • databaseType: it’s normally not necessary to specify this property as it is automatically analyzed from the database connection metadata. Should only be specified in case automatic detection fails. Possible values: {h2, mysql, oracle, postgres, mssql, db2}. 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 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.

3.4. JNDI Datasource Configuration

By default, the database configuration for Activiti is contained within the db.properties files in the WEB-INF/classes of each web application. This isn’t always ideal because it requires users to either modify the db.properties in the Activiti source and recompile the war file, or explode the war and modify the db.properties on every deployment.

By using JNDI (Java Naming and Directory Interface) to obtain the database connection, the connection is fully managed by the Servlet Container and the configuration can be managed outside the war deployment. This also allows more control over the connection parameters than what is provided by the db.properties file.

3.4.1. Configuration

Configuration of the JNDI datasource will differ depending on what servlet container application you are using. The instructions below will work for Tomcat, but for other container applications, please refer to the documentation for your container app.

If using Tomcat, the JNDI resource is configured within $CATALINA_BASE/conf/[enginename]/[hostname]/[warname].xml (for the Activiti UI this will usually be $CATALINA_BASE/conf/Catalina/localhost/activiti-app.xml). The default context is copied from the Activiti war file when the application is first deployed, so if it already exists, you will need to replace it. To change the JNDI resource so that the application connects to MySQL instead of H2, for example, change the file to the following:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<?xml version="1.0" encoding="UTF-8"?> <Context antiJARLocking="true" path="/activiti-app"> <Resource auth="Container" name="jdbc/activitiDB" type="javax.sql.DataSource" description="JDBC DataSource" url="jdbc:mysql://localhost:3306/activiti" driverClassName="com.mysql.jdbc.Driver" username="sa" password="" defaultAutoCommit="false" initialSize="5" maxWait="5000" maxActive="120" maxIdle="5"/> </Context>

3.4.2. JNDI properties

To configure a JNDI Datasource, use following properties in the properties file for the Activiti UI:

  • datasource.jndi.name: the JNDI name of the Datasource.

  • datasource.jndi.resourceRef: Set whether the lookup occurs in a J2EE container, i.e. if the prefix "java:comp/env/" needs to be added if the JNDI name doesn’t already contain it. Default is "true".

3.5. Supported databases

Listed below are the types (case sensitive!) that Activiti uses to refer to databases.

Activiti database type Example JDBC URL Notes

h2

jdbc:h2:tcp://localhost/activiti

Default configured database

mysql

jdbc:mysql://localhost:3306/activiti?autoReconnect=true

Tested using mysql-connector-java database driver

oracle

jdbc:oracle:thin:@localhost:1521:xe

 

postgres

jdbc:postgresql://localhost:5432/activiti

 

db2

jdbc:db2://localhost:50000/activiti

 

mssql

jdbc:sqlserver://localhost:1433;databaseName=activiti (jdbc.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver) OR jdbc:jtds:sqlserver://localhost:1433/activiti (jdbc.driver=net.sourceforge.jtds.jdbc.Driver)

Tested using Microsoft JDBC Driver 4.0 (sqljdbc4.jar) and JTDS Driver

3.6. Creating the database tables

The easiest way to create the database tables for your database is to:

  • Add the activiti-engine jars to your classpath

  • Add a suitable database driver

  • Add an Activiti configuration file (activiti.cfg.xml) to your classpath, pointing to your database (see database configuration section)

  • Execute the main method of the DbSchemaCreate class

However, often only database administrators can execute DDL statements on a database. On a production system, this is also the wisest of choices. The SQL DDL statements can be found on the Activiti downloads page or inside the Activiti distribution folder, in the databasesubdirectory. The scripts are also in the engine jar (activiti-engine-x.jar), in the package org/activiti/db/create (the drop folder contains the drop statements). The SQL files are of the form

activiti.{db}.{create|drop}.{type}.sql

Where db is any of the supported databases and type is

  • engine: the tables needed for engine execution. Required.

  • identity: the tables containing users, groups and memberships of users to groups. These tables are optional and should be used when using the default identity management as shipped with the engine.

  • history: the tables that contain the history and audit information. Optional: not needed when history level is set to none. Note that this will also disable some features (such as commenting on tasks) which store the data in the history database.

Note for MySQL users: MySQL version lower than 5.6.4 has no support for timestamps or dates with millisecond precision. To make things even worse, some version will throw an exception when trying to create such a column but other versions don’t. When doing auto-creation/upgrade, the engine will change the DDL when executing it. When using the DDL file approach, both a regular version and a special file with mysql55 in it are available (this applies on anything lower than 5.6.4). This latter file will have column types with no millisecond precision in it.

Concretely, the following applies for MySQL version

  • <5.6: No millisecond precision available. DDL files available (look for files containing mysql55). Auto creation/update will work out of the box.

  • 5.6.0 - 5.6.3: No millisecond precision available. Auto creation/update will NOT work. It is advised to upgrade to a newer database version anyway. DDL files for mysql 5.5 could be used if really needed.

  • 5.6.4+: Millisecond precision available. DDL files available (default file containing mysql). Auto creation/update works out of the box.

Do note that in the case of upgrading the MySQL database later on and the Activiti tables are already created/upgraded, the column type change will have to be done manually!

3.7. Database table names explained

The database names of Activiti all start with ACT_. The second part is a two-character identification of the use case of the table. This use case will also roughly match the service API.

  • ACT_RE_*: RE stands for repository. Tables with this prefix contain static information such as process definitions and process resources (images, rules, etc.).

  • ACT_RU_*: RU stands for runtime. These are the runtime tables that contain the runtime data of process instances, user tasks, variables, jobs, etc. Activiti only stores the runtime data during process instance execution, and removes the records when a process instance ends. This keeps the runtime tables small and fast.

  • ACT_ID_*: ID stands for identity. These tables contain identity information, such as users, groups, etc.

  • ACT_HI_*: HI stands for history. These are the tables that contain historic data, such as past process instances, variables, tasks, etc.

  • ACT_GE_*: general data, which is used in various use cases.

3.8. Database upgrade

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:

1 2 3 4 5 6 7 8 9
<beans > <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> <!-- ... --> <property name="databaseSchemaUpdate" value="true" /> <!-- ... --> </bean> </beans>

Also, include a suitable database driver for your database to the classpath. Upgrade the Activiti libraries in your application. Or start up a new version of Activiti and point it to a 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.

As an alternative you can also run the upgrade DDL statements. It’s also possible to run the upgrade database scripts, available on the Activiti downloads page.

3.9. Job Executor (since version 6.0.0)

The async executor of Activiti 5 is the only available job executor in Activiti 6 as it is a more performant and more database friendly way of executing asynchronous jobs in the Activiti Engine. The old job executor of Activiti 5 is removed. More information can be found in the advanced section of the user guide.

Moreover, if running under Java EE 7, JSR-236 compliant ManagedAsyncJobExecutor can be used for letting the container manage the threads. In order to enable them, the thread factory should be passed in the configuration as follows:

1 2 3 4 5 6 7 8 9
<bean id="threadFactory" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:jboss/ee/concurrency/factory/default" /> </bean> <bean id="customJobExecutor" class="org.activiti.engine.impl.jobexecutor.ManagedAsyncJobExecutor"> <!-- ... --> <property name="threadFactory" ref="threadFactory" /> <!-- ... --> </bean>

The managed implementation fall back to their default counterparts if the thread factory is not specified.

3.10. Job executor activation

The AsyncExecutor is a component that manages a thread pool to fire timers and other asynchronous tasks. Other implementations are possible (for example using a message queue, see the advanced section of the user guide).

By default, the AsyncExecutor is not activated and not started. With the following configuration the async executor can be started together with the Activiti Engine.

1
<property name="asyncExecutorActivate" value="true" />

The property asyncExecutorActivate instructs the Activiti Engine to startup the Async executor at startup.

3.11. Mail server configuration

Configuring a mail server is optional. Activiti supports sending e-mails in business processes. To actually send an e-mail, a valid SMTP mail server configuration is required. See the e-mail task for the configuration options.

3.12. History configuration

Customizing the configuration of history storage is optional. This allows you to tweak settings that influence the history capabilities of the engine. See history configuration for more details.

1
<property name="history" value="audit" />

3.13. Exposing configuration beans in expressions and scripts

By default, all beans that you specify in the activiti.cfg.xml configuration or in your own Spring configuration file are available to expressions and in the scripts. If you want to limit the visibility of beans in your configuration file, you can configure a property called beansin your process engine configuration. The beans property in ProcessEngineConfiguration is a map. When you specify that property, only beans specified in that map will be visible to expressions and scripts. The exposed beans will be exposed with the names as you specify in that map.

3.14. Deployment cache configuration

All process definition are cached (after they’re parsed) to avoid hitting the database every time a process definition is needed and because process definition data doesn’t change. By default, there is no limit on this cache. To limit the process definition cache, add following property

1
<property name="processDefinitionCacheLimit" value="10" />

Setting this property will swap the default hashmap cache with a LRU cache that has the provided hard limit. Of course, the best value of this property depends on the total amount of process definitions stored and the number of process definitions actually used at runtime by all the runtime process instances.

You can also inject your own cache implementation. This must be a bean that implements the org.activiti.engine.impl.persistence.deploy.DeploymentCache interface:

1 2 3
<property name="processDefinitionCache"> <bean class="org.activiti.MyCache" /> </property>

There is a similar property called knowledgeBaseCacheLimit and knowledgeBaseCache for configuring the rules cache. This is only needed when you use the rules task in your processes.

3.15. Logging

All logging (activiti, spring, mybatis, …​) is routed through SLF4J and allows for selecting the logging-implementation of your choice.

By default no SFL4J-binding jar is present in the activiti-engine dependencies, this should be added in your project in order to use the logging framework of your choice. If no implementation jar is added, SLF4J will use a NOP-logger, not logging anything at all, other than a warning that nothing will be logged. For more info on these bindings http://www.slf4j.org/codes.html#StaticLoggerBinder.

With Maven, add for example a dependency like this (here using log4j), note that you still need to add a version:

1 2 3 4
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </dependency>

The activiti-ui and activiti-rest webapps are configured to use Log4j-binding. Log4j is also used when running the tests for all the activiti-* modules.

Important note when using a container with commons-logging in the classpath: In order to route the spring-logging through SLF4J, a bridge is used (see http://www.slf4j.org/legacy.html#jclOverSLF4J). If your container provides a commons-logging implementation, please follow directions on this page: http://www.slf4j.org/codes.html#release to ensure stability.

Example when using Maven (version omitted):

1 2 3 4
<dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> </dependency>

3.16. Mapped Diagnostic Contexts

Activiti supports Mapped Diagnostic Contexts feature of SLF4j. These basic information are passed to the underlying logger along with what is going to be logged:

  • processDefinition Id as mdcProcessDefinitionID

  • processInstance Id as mdcProcessInstanceID

  • execution Id as mdcExecutionId

None of these information are logged by default. The logger can be configured to show them in desired format, extra to the usual logged messages. For example in Log4j the following sample layout definition causes the logger to show the above mentioned information:

1 2
log4j.appender.consoleAppender.layout.ConversionPattern=ProcessDefinitionId=%X{mdcProcessDefinitionID} executionId=%X{mdcExecutionId} mdcProcessInstanceID=%X{mdcProcessInstanceID} mdcBusinessKey=%X{mdcBusinessKey} %m%n

This is useful when the logs contain information that needs to checked in real time, by means of a log analyzer for example.

3.17. Event handlers

The event mechanism in the Activiti engine allows you to get notified when various events occur within the engine. Take a look at all supported event types for an overview of the events available.

It’s possible to register a listener for certain types of events as opposed to getting notified when any type of event is dispatched. You can either add engine-wide event listeners through the configuration, add engine-wide event listeners at runtime using the API or add event-listeners to specific process definitions in the BPMN XML.

All events dispatched are a subtype of org.activiti.engine.delegate.event.ActivitiEvent. The event exposes (if available) the typeexecutionIdprocessInstanceId and processDefinitionId. Certain events contain additional context related to the event that occurred, additional information about additional payload can be found in the list of all supported event types.

3.17.1. Event listener implementation

The only requirement an event-listener has, is to implement org.activiti.engine.delegate.event.ActivitiEventListener. Below is an example implementation of a listener, which outputs all events received to the standard-out, with exception of events related to job-execution:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
public class MyEventListener implements ActivitiEventListener { @Override public void onEvent(ActivitiEvent event) { switch (event.getType()) { case JOB_EXECUTION_SUCCESS: System.out.println("A job well done!"); break; case JOB_EXECUTION_FAILURE: System.out.println("A job has failed..."); break; default: System.out.println("Event received: " + event.getType()); } } @Override public boolean isFailOnException() { // The logic in the onEvent method of this listener is not critical, exceptions // can be ignored if logging fails... return false; } }

The isFailOnException() method determines the behaviour in case the onEvent(..) method throws an exception when an event is dispatched. In case false is returned, the exception is ignored. When true is returned, the exception is not ignored and bubbles up, effectively failing the current ongoing command. In case the event was part of an API-call (or any other transactional operation, e.g. job-execution), the transaction will be rolled back. In case the behaviour in the event-listener is not business-critical, it’s recommended to return false.

There are a few base implementations provided by Activiti to facilitate common use cases of event-listeners. These can be used as base-class or as an example listener implementation:

  • org.activiti.engine.delegate.event.BaseEntityEventListener: An event-listener base-class that can be used to listen for entity-related events for a specific type of entity or for all entities. It hides away the type-checking and offers 4 methods that should be overridden: onCreate(..)onUpdate(..) and onDelete(..) when an entity is created, updated or deleted. For all other entity-related events, the onEntityEvent(..) is called.

3.17.2. Configuration and setup

If an event-listener is configured in the process engine configuration, it will be active when the process engine starts and will remain active after subsequent reboots of the engine.

The property eventListeners expects a list of org.activiti.engine.delegate.event.ActivitiEventListener instances. As usual, you can either declare an inline bean definition or use a ref to an existing bean instead. The snippet below adds an event-listener to the configuration that is notified when any event is dispatched, regardless of its type:

1 2 3 4 5 6 7 8
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> ... <property name="eventListeners"> <list> <bean class="org.activiti.engine.example.MyEventListener" /> </list> </property> </bean>

To get notified when certain types of events get dispatched, use the typedEventListeners property, which expects a map. The key of a map-entry is a comma-separated list of event-names (or a single event-name). The value of a map-entry is a list of org.activiti.engine.delegate.event.ActivitiEventListener instances. The snippet below adds an event-listener to the configuration, that is notified when a job execution was successful or failed:

1 2 3 4 5 6 7 8 9 10 11 12
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> ... <property name="typedEventListeners"> <map> <entry key="JOB_EXECUTION_SUCCESS,JOB_EXECUTION_FAILURE" > <list> <bean class="org.activiti.engine.example.MyJobEventListener" /> </list> </entry> </map> </property> </bean>

The order of dispatching events is determined on the order the listeners were added. First, all normal event-listeners are called (eventListeners property) in the order they are defined in the list. After that, all typed event listeners (typedEventListenersproperties) are called, if an event of the right type is dispatched.

3.17.3. Adding listeners at runtime

It’s possible to add and remove additional event-listeners to the engine by using the API (RuntimeService):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/** * Adds an event-listener which will be notified of ALL events by the dispatcher. * @param listenerToAdd the listener to add */ void addEventListener(ActivitiEventListener listenerToAdd); /** * Adds an event-listener which will only be notified when an event occurs, which type is in the given types. * @param listenerToAdd the listener to add * @param types types of events the listener should be notified for */ void addEventListener(ActivitiEventListener listenerToAdd, ActivitiEventType... types); /** * Removes the given listener from this dispatcher. The listener will no longer be notified, * regardless of the type(s) it was registered for in the first place. * @param listenerToRemove listener to remove */ void removeEventListener(ActivitiEventListener listenerToRemove);

Please note that the listeners added at runtime are not retained when the engine is rebooted.

3.17.4. Adding listeners to process definitions

It’s possible to add listeners to a specific process-definition. The listeners will only be called for events related to the process definition and to all events related to process instances that are started with that specific process definition. The listener implementations can be defined using a fully qualified classname, an expression that resolves to a bean that implements the listener interface or can be configured to throw a message/signal/error BPMN event.

Listeners executing user-defined logic

The snippet below adds 2 listeners to a process-definition. The first listener will receive events of any type, with a listener implementation based on a fully-qualified class name. The second listener is only notified when a job is successfully executed or when it failed, using a listener that has been defined in the beans property of the process engine configuration.

1 2 3 4 5 6 7 8 9
<process id="testEventListeners"> <extensionElements> <activiti:eventListener class="org.activiti.engine.test.MyEventListener" /> <activiti:eventListener delegateExpression="${testEventListener}" events="JOB_EXECUTION_SUCCESS,JOB_EXECUTION_FAILURE" /> </extensionElements> ... </process>

For events related to entities, it’s also possible to add listeners to a process-definition that get only notified when entity-events occur for a certain entity type. The snippet below shows how this can be achieved. It can be used along for ALL entity-events (first example) or for specific event types only (second example).

1 2 3 4 5 6 7 8 9
<process id="testEventListeners"> <extensionElements> <activiti:eventListener class="org.activiti.engine.test.MyEventListener" entityType="task" /> <activiti:eventListener delegateExpression="${testEventListener}" events="ENTITY_CREATED" entityType="task" /> </extensionElements> ... </process>

Supported values for the entityType are: attachmentcommentexecutionidentity-linkjobprocess-instanceprocess-definitiontask.

Listeners throwing BPMN events

Another way of handling events being dispatched is to throw a BPMN event. Please bear in mind that it only makes sense to throw BPMN-events with certain kinds of Activiti event types. For example, throwing a BPMN event when the process-instance is deleted will result in an error. The snippet below shows how to throw a signal inside process-instance, throw a signal to an external process (global), throw a message-event inside the process-instance and throw an error-event inside the process-instance. Instead of using the class or delegateExpression, the attribute throwEvent is used, along with an additional attribute, specific to the type of event being thrown.

1 2 3 4 5
<process id="testEventListeners"> <extensionElements> <activiti:eventListener throwEvent="signal" signalName="My signal" events="TASK_ASSIGNED" /> </extensionElements> </process>
1 2 3 4 5
<process id="testEventListeners"> <extensionElements> <activiti:eventListener throwEvent="globalSignal" signalName="My signal" events="TASK_ASSIGNED" /> </extensionElements> </process>
1 2 3 4 5
<process id="testEventListeners"> <extensionElements> <activiti:eventListener throwEvent="message" messageName="My message" events="TASK_ASSIGNED" /> </extensionElements> </process>
1 2 3 4 5
<process id="testEventListeners"> <extensionElements> <activiti:eventListener throwEvent="error" errorCode="123" events="TASK_ASSIGNED" /> </extensionElements> </process>

If additional logic is needed to decide whether or not to throw the BPMN-event, it’s possible to extend the listener-classes provided by Activiti. By overriding the isValidEvent(ActivitiEvent event) in your subclass, the BPMN-event throwing can be prevented. The classes involved are +org.activiti.engine.test.api.event.SignalThrowingEventListenerTestorg.activiti.engine.impl.bpmn.helper.MessageThrowingEventListener and org.activiti.engine.impl.bpmn.helper.ErrorThrowingEventListener.

Notes on listeners on a process-definition
  • Event-listeners can only be declared on the process element, as a child-element of the extensionElements. Listeners cannot be defined on individual activities in the process.

  • Expressions used in the delegateExpression do not have access to the execution-context, as other expressions (e.g. in gateways) have. They can only reference beans defined in the beans property of the process engine configuration or when using spring (and the beans property is absent) to any spring-bean that implements the listener interface.

  • When using the class attribute of a listener, there will only be a single instance of that class created. Make sure the listener implementations do not rely on member-fields or ensure safe usage from multiple threads/contexts.

  • When an illegal event-type is used in the events attribute or illegal throwEvent value is used, an exception will be thrown when the process-definition is deployed (effectively failing the deployment). When an illegal value for class or delegateExecution is supplied (either a nonexistent class, a nonexistent bean reference or a delegate not implementing listener interface), an exception will be thrown when the process is started (or when the first valid event for that process-definition is dispatched to the listener). Make sure the referenced classes are on the classpath and that the expressions resolve to a valid instance.

3.17.5. Dispatching events through API

We opened up the event-dispatching mechanism through the API, to allow you to dispatch custom events to any listeners that are registered in the engine. It’s recommended (although not enforced) to only dispatch ActivitiEvents with type CUSTOM. Dispatching the event can be done using the RuntimeService:

1 2 3 4 5 6 7 8 9
/** * Dispatches the given event to any listeners that are registered. * @param event event to dispatch. * * @throws ActivitiException if an exception occurs when dispatching the event or when the {@link ActivitiEventDispatcher} * is disabled. * @throws ActivitiIllegalArgumentException when the given event is not suitable for dispatching. */ void dispatchEvent(ActivitiEvent event);

3.17.6. Supported event types

Listed below are all event types that can occur in the engine. Each type corresponds to an enum value in the org.activiti.engine.delegate.event.ActivitiEventType.

Table 1. Supported events
Event name Description Event classes

ENGINE_CREATED

The process-engine this listener is attached to, has been created and is ready for API-calls.

org.activiti…​ActivitiEvent

ENGINE_CLOSED

The process-engine this listener is attached to, has been closed. API-calls to the engine are no longer possible.

org.activiti…​ActivitiEvent

ENTITY_CREATED

A new entity is created. The new entity is contained in the event.

org.activiti…​ActivitiEntityEvent

ENTITY_INITIALIZED

A new entity has been created and is fully initialized. If any children are created as part of the creation of an entity, this event will be fired AFTER the create/initialisation of the child entities as opposed to the ENTITY_CREATEevent.

org.activiti…​ActivitiEntityEvent

ENTITY_UPDATED

An existing is updated. The updated entity is contained in the event.

org.activiti…​ActivitiEntityEvent

ENTITY_DELETED

An existing entity is deleted. The deleted entity is contained in the event.

org.activiti…​ActivitiEntityEvent

ENTITY_SUSPENDED

An existing entity is suspended. The suspended entity is contained in the event. Will be dispatched for ProcessDefinitions, ProcessInstances and Tasks.

org.activiti…​ActivitiEntityEvent

ENTITY_ACTIVATED

An existing entity is activated. The activated entity is contained in the event. Will be dispatched for ProcessDefinitions, ProcessInstances and Tasks.

org.activiti…​ActivitiEntityEvent

JOB_EXECUTION_SUCCESS

A job has been executed successfully. The event contains the job that was executed.

org.activiti…​ActivitiEntityEvent

JOB_EXECUTION_FAILURE

The execution of a job has failed. The event contains the job that was executed and the exception.

org.activiti…​ActivitiEntityEvent and org.activiti…​ActivitiExceptionEvent

JOB_RETRIES_DECREMENTED

The number of job retries have been decremented due to a failed job. The event contains the job that was updated.

org.activiti…​ActivitiEntityEvent

TIMER_FIRED

A timer has been fired. The event contains the job that was executed?

org.activiti…​ActivitiEntityEvent

JOB_CANCELED

A job has been canceled. The event contains the job that was canceled. Job can be canceled by API call, task was completed and associated boundary timer was canceled, on the new process definition deployment.

org.activiti…​ActivitiEntityEvent

ACTIVITY_STARTED

An activity is starting to execute

org.activiti…​ActivitiActivityEvent

ACTIVITY_COMPLETED

An activity is completed successfully

org.activiti…​ActivitiActivityEvent

ACTIVITY_CANCELLED

An activity is going to be cancelled. There can be three reasons for activity cancellation (MessageEventSubscriptionEntity, SignalEventSubscriptionEntity, TimerEntity).

org.activiti…​ActivitiActivityCancelledEvent

ACTIVITY_SIGNALED

An activity received a signal

org.activiti…​ActivitiSignalEvent

ACTIVITY_MESSAGE_RECEIVED

An activity received a message. Dispatched before the activity receives the message. When received, a ACTIVITY_SIGNAL or ACTIVITY_STARTED will be dispatched for this activity, depending on the type (boundary-event or event-subprocess start-event)

org.activiti…​ActivitiMessageEvent

ACTIVITY_ERROR_RECEIVED

An activity has received an error event. Dispatched before the actual error has been handled by the activity. The event’s activityId contains a reference to the error-handling activity. This event will be either followed by a ACTIVITY_SIGNALLED event or ACTIVITY_COMPLETE for the involved activity, if the error was delivered successfully.

org.activiti…​ActivitiErrorEvent

UNCAUGHT_BPMN_ERROR

An uncaught BPMN error has been thrown. The process did not have any handlers for that specific error. The event’s activityId will be empty.

org.activiti…​ActivitiErrorEvent

ACTIVITY_COMPENSATE

An activity is about to be compensated. The event contains the id of the activity that is will be executed for compensation.

org.activiti…​ActivitiActivityEvent

VARIABLE_CREATED

A variable has been created. The event contains the variable name, value and related execution and task (if any).

org.activiti…​ActivitiVariableEvent

VARIABLE_UPDATED

An existing variable has been updated. The event contains the variable name, updated value and related execution and task (if any).

org.activiti…​ActivitiVariableEvent

VARIABLE_DELETED

An existing variable has been deleted. The event contains the variable name, last known value and related execution and task (if any).

org.activiti…​ActivitiVariableEvent

TASK_ASSIGNED

A task has been assigned to a user. The event contains the task

org.activiti…​ActivitiEntityEvent

TASK_CREATED

A task has been created. This is dispatched after the ENTITY_CREATE event. In case the task is part of a process, this event will be fired before the task listeners are executed.

org.activiti…​ActivitiEntityEvent

TASK_COMPLETED

A task has been completed. This is dispatched before the ENTITY_DELETE event. In case the task is part of a process, this event will be fired before the process has moved on and will be followed by a ACTIVITY_COMPLETE event, targeting the activity that represents the completed task.

org.activiti…​ActivitiEntityEvent

PROCESS_COMPLETED

A process has been completed. Dispatched after the last activity ACTIVITY_COMPLETEDevent. Process is completed when it reaches state in which process instance does not have any transition to take.

org.activiti…​ActivitiEntityEvent

PROCESS_CANCELLED

A process has been cancelled. Dispatched before the process instance is deleted from runtime. Process instance is cancelled by API call RuntimeService.deleteProcessInstance

org.activiti…​ActivitiCancelledEvent

MEMBERSHIP_CREATED

A user has been added to a group. The event contains the ids of the user and group involved.

org.activiti…​ActivitiMembershipEvent

MEMBERSHIP_DELETED

A user has been removed from a group. The event contains the ids of the user and group involved.

org.activiti…​ActivitiMembershipEvent

MEMBERSHIPS_DELETED

All members will be removed from a group. The event is thrown before the members are removed, so they are still accessible. No individual MEMBERSHIP_DELETED events will be thrown if all members are deleted at once, for performance reasons.

org.activiti…​ActivitiMembershipEvent

All ENTITY_\* events are related to entities inside the engine. The list below show an overview of what entity-events are dispatched for which entities:

  • ENTITY_CREATED, ENTITY_INITIALIZED, ENTITY_DELETED: Attachment, Comment, Deployment, Execution, Group, IdentityLink, Job, Model, ProcessDefinition, ProcessInstance, Task, User.

  • ENTITY_UPDATED: Attachment, Deployment, Execution, Group, IdentityLink, Job, Model, ProcessDefinition, ProcessInstance, Task, User.

  • ENTITY_SUSPENDED, ENTITY_ACTIVATED: ProcessDefinition, ProcessInstance/Execution, Task.

3.17.7. 其它备注

Only listeners are notified in the engine the events are dispatched from. So in case you have different engines - running against the same database - only events that originated in the engine the listener is registered for, are dispatched to that listener. The events that occur in the other engine are not dispatched to the listeners, regardless of the fact they are running in the same JVM or not.

Certain event-types (related to entities) expose the targeted entity. Depending on the type or event, these entities cannot be updated anymore (e.g. when the entity is deleted). If possible, use the EngineServices exposed by the event to interact in a safe way with the engine. Even then, you need to be cautious with updates/operations on entities that are involved in the dispatched event.

No entity-events are dispatched related to history, as they all have a runtime-counterpart which have their events dispatched.

猜你喜欢

转载自www.cnblogs.com/icoolno1/p/8970406.html
今日推荐