Activiti 5.16 简介 学习 配置

Activiti 5.16 用户手册

Chapter 1-2-3 简介 学习 配置

Table of Contents

协议
下载
源码
必要的软件
JDK 6+
Eclipse Indigo 和 Juno
报告问题
试验性功能
内部实现类

协议

Activiti是基于Apache V2协议发布的。

下载

http://activiti.org/download.html

源码

发布包里包含大部分的已经打好jar包的源码。 如果想找到并构建完整的源码库,请参考 wiki “构建发布包”。

必要的软件

JDK 6+

Activiti需要运行在JDK 6或以上版本上。 进入 Oracle Java SE 下载页面 点击 “下载 JDK”按钮。页面上也提供了安装的方法。 为了验证是否安装成功,可以在命令行中执行 java -version。 它将会打印出安装的JDK的版本。

Eclipse Indigo 和 Juno

(译者注:Eclipse 3.7 版本代号 Indigo 靛青, Eclipse 4.2 版本代号 Juno 朱诺)。 在Eclipse下载页面下载你选择的eclipse发布包。 解压下载文件,你就可以通过eclipse目录下的eclipse文件启动它。 此外,在该用户指南后面,专门有一章介绍安装eclipse设计器插件。

Chapter 2. 开始学习

一分钟入门

从Activiti网站下载Activiti Explorer的WAR文件后, 可以按照下列步骤以默认配置运行样例。 你需要一个Java 运行环境和 Apache Tomcat (其实,任何提供了servlet功能的web容器都可以正常运行。但是我们主要是使用tomcat进行的测试)。

把下载的activiti-explorer.war复制到Tomcat的webapps目录下。

执行Tomcat的bin目录下的startup.bat或startup.sh启动服务器。

Tomcat启动后,打开浏览器访问http://localhost:8080/activiti-explorer。 使用kermit/kermit登录。

这样就好了!Activiti Explorer默认使用H2内存数据库,如果你想使用其他数据库 请参考这里。

安装Activiti

要安装Activiti你需要一个 Java运行环境 和 Apache Tomcat。 还要确认设置好JAVA_HOME系统变量。 不同的操作系统下的设置方法是不同的。

要运行Activiti Explorer和REST web应用,你要从Activiti的下载页下载WAR文件, 复制到Tomcat安装目录下webapps目录下。 默认Explorer应用使用的内存数据库已经包含了示例流程,用户和群组信息。

下面是示例中可以使用的用户:

Table 2.1. 示例用户

账号 密码 角色
kermit kermit admin
gonzo gonzo manager
fozzie fozzie user

现在,你可以访问下列web应用:

Table 2.2. webapp工具

Webapp名称 URL 描述
Activiti Explorer http://localhost:8080/activiti-explorer 流程引擎的用户控制台。使用它来启动新流程,分配任务, 查看并认领任务,等等。这个工具也可以用来管理Activiti引擎。

注意Activiti Explorer演示实例只是一种简单快速展示Activiti的功能的方式。 但是并不是说只能使用这种方式使用Activiti。 Activiti只是一个jar, 可以内嵌到任何Java环境中:swing或者Tomcat, JBoss, WebSphere等等。 也可以把Activiti作为一个典型的单独运行的BPM服务器运行。 只要java可以做的,Activiti也可以。

安装Activiti数据库

就像在一分钟入门里说过的,Activiti Explorer默认使用H2内存数据库。 要让Activiti使用独立运行的H2数据库或者其他数据库, 可以修改Activiti Explorer web应用WEB-INF/classes目录下的db.properties。

另外,注意Activiti Explorer自动生成了演示用的默认用户和群组,流程定义,数据模型。 要想禁用这个功能,要修改WEB-INF目录下的activiti-standalone-context.xml。 可以使用下面的demoDataGenerator bean定义代码完全禁用安装默认数据。从代码中也可以看出,我们可以单独启用或禁用每一项功能。

1
2
3
4
5
6
<bean id="demoDataGenerator" class="org.activiti.explorer.demo.DemoDataGenerator">
<property name="processEngine" ref="processEngine" />
<property name="createDemoUsersAndGroups" value="false" />
<property name="createDemoProcessDefinitions" value="false" />
<property name="createDemoModels" value="false" />
</bean>

引入Activiti jar和依赖

为了引用Activiti jar和依赖,我们推荐使用 Maven(或Ivy), 它简化了我们之间的依赖管理。 参考http://www.activiti.org/community.html#maven.repository 来为你的项目引入必须的jar包。

如果不想用Maven,你也可以自己把这些jar引入到你的项目中。 Activiti下载zip包包含了一个libs目录, 包含了所有Activiti的jar包(和源代码jar包)。依赖没有用这种方式发布。 Activiti引擎必须的依赖如下所示(通过mvn dependency:tree生成):

1
2
3
4
5
6
7
8
9
10
11
org.activiti:activiti-engine:jar:5.12.1
+- org.apache.commons:commons-email:jar:1.2:compile
| +- javax.mail:mail:jar:1.4.1:compile
| \- javax.activation:activation:jar:1.1:compile
+- org.apache.commons:commons-lang3:jar:3.1:compile
+- org.mybatis:mybatis:jar:3.1.1:compile
+- org.springframework:spring-beans:jar:3.1.2.RELEASE:compile
| \- org.springframework:spring-core:jar:3.1.2.RELEASE:compile
| +- org.springframework:spring-asm:jar:3.1.2.RELEASE:compile
| \- commons-logging:commons-logging:jar:1.1.1:compile
\- joda-time:joda-time:jar:2.1:compile

注意:只有使用了mail service task才必须引入mail依赖jar。

所有依赖可以在Activiti 源码的模块中, 通过mvn dependency:copy-dependencies下载。

下一步

使用Activiti Explorer web应用 是一个熟悉Activiti概念和功能的好办法。但是, Activiti的主要目标是为你自己的应用添加强大的BPM和工作流功能。 下面的章节会帮助你熟悉 如何在你的环境中使用Activiti进行编程:

  • 配置章节 会教你如何设置Activiti, 如何获得ProcessEngine类的实例, 它是所有Activiti引擎功能的中心入口。

  • API章节会带领你了解建立Activiti API的服务。 这些服务用简便的方法提供了Activiti引擎的强大功能, 它们可以使用在任何Java环境下。

  • 对深入了解BPMN 2.0,Activiti引擎中流程的编写结构感兴趣吗? 请继续浏览BPMN 2.0 章节。

Chapter 3. 配置

创建ProcessEngine

Activiti流程引擎的配置文件是名为activiti.cfg.xml的XML文件。 注意这与使用Spring方式创建流程引擎 是不一样的。

获得ProcessEngine最简单的办法是 使用org.activiti.engine.ProcessEngines类:

1
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine()

它会在classpath下搜索activiti.cfg.xml, 并基于这个文件中的配置构建引擎。 下面代码展示了实例配置。 后面的章节会给出配置参数的详细介绍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<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>

注意配置XML文件其实是一个spring的配置文件。 但不是说Activiti只能用在Spring环境中! 我们只是利用了Spring的解析和依赖注入功能 来构建引擎。

配置文件中使用的ProcessEngineConfiguration可以通过编程方式创建。 可以配置不同的bean id(比如,第三行)。

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

也可以不使用配置文件,基于默认创建配置 (参考各种支持类)

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")
.setJobExecutorActivate(true)
.buildProcessEngine();

ProcessEngineConfiguration bean

activiti.cfg.xml必须包含一个id为’processEngineConfiguration’的bean。

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

这个bean会用来构建ProcessEngine。 有多个类可以用来定义processEngineConfiguration。 这些类对应不同的环境,并设置了对应的默认值。 最好选择(最)适用于你的环境的类, 这样可以少配置几个引擎的参数。 下面是目前可以使用的类(以后会包含更多):

  • org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration: 单独运行的流程引擎。Activiti会自己处理事务。 默认,数据库只在引擎启动时检测 (如果没有Activiti的表或者表结构不正确就会抛出异常)。
  • org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration: 单元测试时的辅助类。Activiti会自己控制事务。 默认使用H2内存数据库。数据库表会在引擎启动时创建,关闭时删除。 使用它时,不需要其他配置(除非使用job执行器或邮件功能)。
  • org.activiti.spring.SpringProcessEngineConfiguration: 在Spring环境下使用流程引擎。 参考Spring集成章节。
  • org.activiti.engine.impl.cfg.JtaProcessEngineConfiguration: 单独运行流程引擎,并使用JTA事务。

数据库配置

Activiti可能使用两种方式配置数据库。 第一种方式是定义数据库配置参数:

  • jdbcUrl: 数据库的JDBC URL。
  • 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="" />

也可以使用javax.sql.DataSource。 (比如,Apache Commons的DBCP):

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

注意,Activiti的发布包中没有这些类。 你要自己把对应的类(比如,从DBCP里)放到你的classpath下。

无论你使用JDBC还是DataSource的方式,都可以设置下面的配置:

  • databaseType: 一般不用设置,因为可以自动通过数据库连接的元数据获取。 只有自动检测失败时才需要设置。 可能的值有:{h2, mysql, oracle, postgres, mssql, db2}。 如果没使用默认的H2数据库就必须设置这项。 这个配置会决定使用哪些创建/删除脚本和查询语句。 参考支持数据库章节 了解支持哪些类型。
  • databaseSchemaUpdate: 设置流程引擎启动和关闭时如何处理数据库表。
    • false(默认):检查数据库表的版本和依赖库的版本, 如果版本不匹配就抛出异常。
    • true: 构建流程引擎时,执行检查,如果需要就执行更新。 如果表不存在,就创建。
    • create-drop: 构建流程引擎时创建数据库表, 关闭流程引擎时删除这些表。

JNDI数据库配置

默认,Activiti的数据库配置会放在web应用的WEB-INF/classes目录下的db.properties文件中。 这样做比较繁琐, 因为要用户在每次发布时,都修改Activiti源码中的db.properties并重新编译war文件, 或者解压缩war文件,修改其中的db.properties。

使用JNDI(Java命名和目录接口)来获取数据库连接, 连接是由servlet容器管理的,可以在war部署外边管理配置。 与db.properties相比, 它也允许对连接进行更多的配置。

使用

要想把Activiti Explorer和Activiti Rest应用从db.properties转换为使用 JNDI数据库配置,需要打开原始的Spring配置文件 (activiti-webapp-explorer2/src/main/webapp/WEB-INF/activiti-standalone-context.xml 和activiti-webapp-rest2/src/main/resources/activiti-context.xml), 删除”dbProperties”和”dataSource”两个bean,然后添加如下bean:

1
2
3
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/activitiDB"/>
</bean>

接下来,我们需要添加包含了默认的H2配置的context.xml文件。 如果已经有了JNDI配置,会覆盖这些配置。 对Activiti Explorer来说,对应的配置文件activiti-webapp-explorer2/src/main/webapp/META-INF/context.xml 如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<Context antiJARLocking="true" path="/activiti-explorer2">
<Resource auth="Container"
name="jdbc/activitiDB"
type="javax.sql.DataSource"
scope="Shareable"
description="JDBC DataSource"
url="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000"
driverClassName="org.h2.Driver"
username="sa"
password=""
defaultAutoCommit="false"
initialSize="5"
maxWait="5000"
maxActive="120"
maxIdle="5"/>
</Context>

对于Activiti REST应用,添加的activiti-webapp-rest2/src/main/webapp/META-INF/context.xml 如下所示:

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

可选的一步,现在可以删除Activiti Explorer和Activiti Rest两个应用中 不再使用的db.properties文件了。

配置

JNDI数据库配置会因为你使用的Servlet container不同而不同。 下面的配置可以在tomcat中使用,但是对其他容器, 请引用你使用的容器的文档。

如果使用tomcat,JNDI资源配置在 $CATALINA_BASE/conf/[enginename]/[hostname]/[warname].xml (对于Activiti Explorer来说,通常是在$CATALINA_BASE/conf/Catalina/localhost/activiti-explorer.war)。 当应用第一次发布时,会把这个文件从war中复制出来。 所以如果这个文件已经存在了,你需要替换它。要想修改JNDI资源让应用连接mysql而不是H2, 可以像下面这样修改:

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-explorer2">
<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>

支持的数据库

下面列出Activiti使用的数据库类型(大小写敏感)。

Table 3.1. 支持的数据库

Activiti数据库类型 JDBC URL实例 备注
h2 jdbc:h2:tcp://localhost/activiti 默认配置的数据库
mysql jdbc:mysql://localhost:3306/activiti?autoReconnect=true 使用mysql-connector-java驱动测试
oracle jdbc:oracle:thin:@localhost:1521:xe
postgres jdbc:postgresql://localhost:5432/activiti
db2 dbc:db2://localhost:50000/activiti
mssql jdbc:sqlserver://localhost:1433/activiti

创建数据库表

下面是创建数据库表最简单的办法:

  • 把activiti-engine的jar放到classpath下
  • 添加对应的数据库驱动
  • 把Activiti配置文件 (activiti.cfg.xml) 放到 classpath下, 指向你的数据库(参考数据库配置章节)
  • 执行 DbSchemaCreate 类的main方法

不过,一般情况只有数据库管理员才能执行DDL语句。 在生产环境,这也是最明智的选择。 SQL DDL语句可以从Activiti下载页或Activiti发布目录里找到,在database子目录下。 脚本也包含在引擎的jar中(activiti-engine-x.jar), 在org/activiti/db/create包下(drop目录里是删除语句)。 SQL文件的命名方式如下

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

其中 db 是 支持的数据库, type 是

  • engine: 引擎执行的表。必须。
  • identity: 包含用户,群组,用户与组之间的关系的表。 这些表是可选的,只有使用引擎自带的默认身份管理时才需要。
  • history: 包含历史和审计信息的表。可选的:历史级别设为none时不会使用。 注意这也会引用一些需要把数据保存到历史表中的功能(比如任务的评论)。

MySQL用户需要注意: 版本低于5.6.4的MySQL不支持毫秒精度的timstamp或date类型。 更严重的是,有些版本会在尝试创建这样一列时抛出异常,而有些版本则不会。 在执行自动创建/更新时,引擎会在执行过程中修改DDL。 当使用DDL时,可以选择通用版本和名为mysql55的文件。 (它适合所有版本低于5.6.4的情况)。 后一个文件会将列的类型设置为没有毫秒的情况。

总结一下,对于MySQL版本会执行如下操作

  • <5.6: 不支持毫秒精度。可以使用DDL文件(包含mysql55的文件)。可以实现自动创建/更新。
  • 5.6.0 - 5.6.3: 不支持毫秒精度。无法自动创建/更新。建议更新到新的数据库版本。如果真的需要的话,也可以使用mysql 5.5。
  • 5.6.4+:支持毫秒精度。可以使用DDL文件(默认包含mysql的文件)。可以实现自动创建、更新。

注意对于已经更新了MySQL数据库,而且Activiti表已经创建/更新的情况, 必须手工修改列的类型。

理解数据库表的命名

Activiti的表都以ACT_开头。 第二部分是表示表的用途的两个字母标识。 用途也和服务的API对应。

  • ACT_RE_*: ‘RE’表示repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
  • ACT_RU_*: ‘RU’表示runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
  • ACT_ID_*: ‘ID’表示identity。 这些表包含身份信息,比如用户,组等等。
  • ACT_HI_*: ‘HI’表示history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
  • ACT_GE_*: 通用数据, 用于不同场景下。

数据库升级

在执行更新之前要先备份数据库 (使用数据库的备份功能)

默认,每次构建流程引擎时都会进行版本检测。 这一切都在应用启动或Activiti webapp启动时发生。 如果Activiti发现数据库表的版本与依赖库的版本不同, 就会抛出异常。

要升级,你要把下面的配置 放到activiti.cfg.xml配置文件里:

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>

然后,把对应的数据库驱动放到classpath里。 升级应用的Activiti依赖。启动一个新版本的Activiti 指向包含旧版本的数据库。将databaseSchemaUpdate设置为true, Activiti会自动将数据库表升级到新版本, 当发现依赖和数据库表版本不通过时。

也可以执行更新升级DDL语句。 也可以执行数据库脚本,可以在Activiti下载页找到。

启用Job执行器

JobExecutor是管理一系列线程的组件,可以触发定时器(也包含后续的异步消息)。 在单元测试场景下,很难使用多线程。因此API允许查询(ManagementService.createJobQuery)和执行job (ManagementService.executeJob),所以job可以在单元测试中控制。 要避免与job执行器冲突,可以关闭它。

默认,JobExecutor在流程引擎启动时就会激活。 如果不想在流程引擎启动后自动激活JobExecutor,可以设置

1
<property name="jobExecutorActivate" value="false" />

配置邮件服务器

可以选择配置邮件服务器。Activiti支持在业务流程中发送邮件。 想真正的发送一个email,必须配置一个真实的SMTP邮件服务器。 参考e-mail任务。

配置历史

可以选择定制历史存储的配置。你可以通过配置影响引擎的历史功能。 参考历史配置。

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

为表达式和脚本暴露配置

默认,activiti.cfg.xml和你自己的Spring配置文件中所有bean 都可以在表达式和脚本中使用。 如果你想限制配置文件中的bean的可见性, 可以配置流程引擎配置的beans配置。 ProcessEngineConfiguration的beans是一个map。当你指定了这个参数, 只有包含这个map中的bean可以在表达式和脚本中使用。 通过在map中指定的名称来决定暴露的bean。

配置部署缓存

所有流程定义都被缓存了(解析之后)避免每次使用前都要访问数据库, 因为流程定义数据是不会改变的。 默认,不会限制这个缓存。如果想限制流程定义缓存,可以添加如下配置

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

这个配置会把默认的hashmap缓存替换成LRU缓存,来提供限制。 当然,这个配置的最佳值跟流程定义的总数有关, 实际使用中会具体使用多少流程定义也有关。

你也可以注入自己的缓存实现。这个bean必须实现 org.activiti.engine.impl.persistence.deploy.DeploymentCache接口:

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

有一个类似的配置叫knowledgeBaseCacheLimit和knowledgeBaseCache, 它们是配置规则缓存的。只有流程中使用规则任务时才会用到。

日志

从Activiti 5.12开始,SLF4J被用作日志框架,替换了之前使用java.util.logging。 所有日志(activiti, spring, mybatis等等)都转发给SLF4J 允许使用你选择的日志实现。

默认activiti-engine依赖中没有提供SLF4J绑定的jar, 需要根据你的实际需要使用日志框架。如果没有添加任何实现jar,SLF4J会使用NOP-logger,不使用任何日志,不会发出警告,而且什么日志都不会记录。 可以通过http://www.slf4j.org/codes.html#StaticLoggerBinder了解这些实现。

使用Maven,比如使用一个依赖(这里使用log4j),注意你还需要添加一个version:

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

activiti-explorer和activiti-rest应用都使用了Log4j绑定。执行所有activiti-*模块的单元测试页使用了Log4j。

特别提醒如果容器classpath中存在commons-logging: 为了把spring日志转发给SLF4J,需要使用桥接(参考http://www.slf4j.org/legacy.html#jclOverSLF4J)。 如果你的容器提供了commons-logging实现,请参考下面网页:http://www.slf4j.org/codes.html#release来确保稳定性。

使用Maven的实例(忽略版本):

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

映射诊断上下文

在5.13中,activiti支持slf4j的MDC功能。 如下的基础信息会传递到日志中记录:

  • 流程定义Id标记为mdcProcessDefinitionID
  • 流程实例Id标记为mdcProcessInstanceID
  • 分支Id标记为mdcexecutionId

默认不会记录这些信息。可以配置日志使用期望的格式来显示它们,扩展通常的日志信息。 比如,下面的log4j配置定义会让日志显示上面提及的信息:

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

当系统进行高风险任务,日志必须严格检查时,这个功能就非常有用,比如要使用日志分析的情况。

事件处理

Activiti 5.15中实现了一种事件机制。它允许在引擎触发事件时获得提醒。 参考所有支持的事件类型了解有效的事件。

可以为对应的事件类型注册监听器,在这个类型的任何时间触发时都会收到提醒。 你可以添加引擎范围的事件监听器通过配置, 添加引擎范围的事件监听器在运行阶段使用API, 或添加event-listener到特定流程定义的BPMN XML中。

所有分发的事件,都是org.activiti.engine.delegate.event.ActivitiEvent的子类。事件包含(如果有效)type,executionId,processInstanceId和processDefinitionId。 对应的事件会包含事件发生时对应上下文的额外信息, 这些额外的载荷可以在支持的所有事件类型中找到。

事件监听器实现

实现事件监听器的唯一要求是实现org.activiti.engine.delegate.event.ActivitiEventListener。下面是一个实现监听器的例子,它会把所有监听到的事件打印到标准输出中,包括job执行的事件异常:

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

isFailOnException()方法决定了当事件分发时,onEvent(..)方法抛出异常时的行为。 这里返回的是false,会忽略异常。 当返回true时,异常不会忽略,继续向上传播,迅速导致当前命令失败。 当事件是一个API调用的一部分时(或其他事务性操作,比如job执行), 事务就会回滚。当事件监听器中的行为不是业务性时,建议返回false。

activiti提供了一些基础的实现,实现了事件监听器的常用场景。可以用来作为基类或监听器实现的样例:

  • org.activiti.engine.delegate.event.BaseEntityEventListener: 这个事件监听器的基类可以用来监听实体相关的事件,可以针对某一类型实体,也可以是全部实体。 它隐藏了类型检测,并提供了三个需要重写的方法:onCreate(..), onUpdate(..) 和 onDelete(..),当实体创建,更新,或删除时调用。对于其他实体相关的事件,会调用 onEntityEvent(..)。
配置与安装

把事件监听器配置到流程引擎配置中时,会在流程引擎启动时激活,并在引擎启动启动中持续工作着。

eventListeners属性需要org.activiti.engine.delegate.event.ActivitiEventListener的队列。 通常,我们可以声明一个内部的bean定义,或使用ref引用已定义的bean。 下面的代码,向配置添加了一个事件监听器,任何事件触发时都会提醒它,无论事件是什么类型:

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>

为了监听特定类型的事件,可以使用typedEventListeners属性,它需要一个map参数。 map的key是逗号分隔的事件名(或单独的事件名)。 map的value是org.activiti.engine.delegate.event.ActivitiEventListener队列。 下面的代码演示了向配置中添加一个事件监听器,可以监听job执行成功或失败:

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>

分发事件的顺序是由监听器添加时的顺序决定的。首先,会调用所有普通的事件监听器(eventListeners属性),按照它们在list中的次序。然后,会调用所有对应类型的监听器(typedEventListeners属性),如果对应类型的事件被触发了。

在运行阶段添加监听器

可以通过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);

注意运行期添加的监听器引擎重启后就消失了。

为流程定义添加监听器

可以为特定流程定义添加监听器。监听器只会监听与这个流程定义相关的事件,以及这个流程定义上发起的所有流程实例的事件。 监听器实现可以使用,全类名定义,引用实现了监听器接口的表达式,或配置为抛出一个message/signal/error的BPMN事件。

让监听器执行用户定义的逻辑

下面代码为一个流程定义添加了两个监听器。第一个监听器会接收所有类型的事件,它是通过全类名定义的。第二个监听器只接收作业成功或失败的事件,它使用了定义在流程引擎配置中的beans属性中的一个bean。

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>

对于实体相关的事件,也可以设置为针对某个流程定义的监听器,实现只监听发生在某个流程定义上的某个类型实体事件。下面的代码演示了如何实现这种功能。可以用于所有实体事件(第一个例子),也可以只监听特定类型的事件(第二个例子)。

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>

entityType支持的值有:attachment, comment, execution,identity-link, job, process-instance, process-definition, task。

监听抛出BPMN事件

另一种处理事件的方法是抛出一个BPMN事件。请注意它只针对与抛出一个activiti事件类型的BPMN事件。 比如,抛出一个BPMN事件,在流程实例删除时,会导致一个错误。 下面的代码演示了如何在流程实例中抛出一个signal,把signal抛出到外部流程(全局),在流程实例中抛出一个消息事件, 在流程实例中抛出一个错误事件。除了使用class或delegateExpression, 还使用了throwEvent属性,通过额外属性,指定了抛出事件的类型。

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>

如果需要声明额外的逻辑,是否抛出BPMN事件,可以扩展activiti提供的监听器类。在子类中重写isValidEvent(ActivitiEvent event), 可以防止抛出BPMN事件。对应的类是org.activiti.engine.test.api.event.SignalThrowingEventListenerTest, org.activiti.engine.impl.bpmn.helper.MessageThrowingEventListener 和 org.activiti.engine.impl.bpmn.helper.ErrorThrowingEventListener.

流程定义中监听器的注意事项
  • 事件监听器只能声明在process元素中,作为extensionElements的子元素。 监听器不能定义在流程的单个activity下。
  • delegateExpression中的表达式无法访问execution上下文,这与其他表达式不同(比如gateway)。 它只能引用定义在流程引擎配置的beans属性中声明的bean,或者使用spring(未使用beans属性)中所有实现了监听器接口的spring-bean。
  • 在使用监听器的 class 属性时,只会创建一个实例。记住监听器实现不会依赖成员变量, 确认是多线程安全的。
  • 当一个非法的事件类型用在events属性或throwEvent中时,流程定义发布时就会抛出异常。(会导致部署失败)。如果class或delegateExecution由问题(类不存在,不存在的bean引用,或代理类没有实现监听器接口),会在流程启动时抛出异常(或在第一个有效的流程定义事件被监听器接收时)。所以要保证引用的类正确的放在classpath下,表达式也要引用一个有效的实例。
通过API分发事件

我们提供了通过API使用事件机制的方法,允许大家触发定义在引擎中的任何自定义事件。 建议(不强制)只触发类型为CUSTOM的ActivitiEvents。可以通过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);
支持的事件类型

下面是引擎中可能出现的所有事件类型。每个类型都对应org.activiti.engine.delegate.event.ActivitiEventType中的一个枚举值。

Table 3.2. 支持的事件

事件名称 描述 事件类型
ENGINE_CREATED 监听器监听的流程引擎已经创建完毕,并准备好接受API调用。 org.activiti…ActivitiEvent
ENGINE_CLOSED 监听器监听的流程引擎已经关闭,不再接受API调用。 org.activiti…ActivitiEvent
ENTITY_CREATED 创建了一个新实体。实体包含在事件中。 org.activiti…ActivitiEntityEvent
ENTITY_INITIALIZED 创建了一个新实体,初始化也完成了。如果这个实体的创建会包含子实体的创建,这个事件会在子实体都创建/初始化完成后被触发,这是与ENTITY_CREATED的区别。 org.activiti…ActivitiEntityEvent
ENTITY_UPDATED 更新了已存在的实体。实体包含在事件中。 org.activiti…ActivitiEntityEvent
ENTITY_DELETED 删除了已存在的实体。实体包含在事件中。 org.activiti…ActivitiEntityEvent
ENTITY_SUSPENDED 暂停了已存在的实体。实体包含在事件中。会被ProcessDefinitions, ProcessInstances 和 Tasks抛出。 org.activiti…ActivitiEntityEvent
ENTITY_ACTIVATED 激活了已存在的实体,实体包含在事件中。会被ProcessDefinitions, ProcessInstances 和 Tasks抛出。 org.activiti…ActivitiEntityEvent
JOB_EXECUTION_SUCCESS 作业执行成功。job包含在事件中。 org.activiti…ActivitiEntityEvent
JOB_EXECUTION_FAILURE 作业执行失败。作业和异常信息包含在事件中。 org.activiti…ActivitiEntityEvent and org.activiti…ActivitiExceptionEvent
JOB_RETRIES_DECREMENTED 因为作业执行失败,导致重试次数减少。作业包含在事件中。 org.activiti…ActivitiEntityEvent
TIMER_FIRED 触发了定时器。job包含在事件中。 org.activiti…ActivitiEntityEvent
JOB_CANCELED 取消了一个作业。事件包含取消的作业。作业可以通过API调用取消, 任务完成后对应的边界定时器也会取消,在新流程定义发布时也会取消。 org.activiti…ActivitiEntityEvent
ACTIVITY_STARTED 一个节点开始执行 org.activiti…ActivitiActivityEvent
ACTIVITY_COMPLETED 一个节点成功结束 org.activiti…ActivitiActivityEvent
ACTIVITY_SIGNALED 一个节点收到了一个信号 org.activiti…ActivitiSignalEvent
ACTIVITY_MESSAGE_RECEIVED 一个节点收到了一个消息。在节点收到消息之前触发。收到后,会触发ACTIVITY_SIGNAL或ACTIVITY_STARTED,这会根据节点的类型(边界事件,事件子流程开始事件) org.activiti…ActivitiMessageEvent
ACTIVITY_ERROR_RECEIVED 一个节点收到了一个错误事件。在节点实际处理错误之前触发。 事件的activityId对应着处理错误的节点。 这个事件后续会是ACTIVITY_SIGNALLED或ACTIVITY_COMPLETE, 如果错误发送成功的话。 org.activiti…ActivitiErrorEvent
UNCAUGHT_BPMN_ERROR 抛出了未捕获的BPMN错误。流程没有提供针对这个错误的处理器。 事件的activityId为空。 org.activiti…ActivitiErrorEvent
ACTIVITY_COMPENSATE 一个节点将要被补偿。事件包含了将要执行补偿的节点id。 org.activiti…ActivitiActivityEvent
VARIABLE_CREATED 创建了一个变量。事件包含变量名,变量值和对应的分支或任务(如果存在)。 org.activiti…ActivitiVariableEvent
VARIABLE_UPDATED 更新了一个变量。事件包含变量名,变量值和对应的分支或任务(如果存在)。 org.activiti…ActivitiVariableEvent
VARIABLE_DELETED 删除了一个变量。事件包含变量名,变量值和对应的分支或任务(如果存在)。 org.activiti…ActivitiVariableEvent
TASK_ASSIGNED 任务被分配给了一个人员。事件包含任务。 org.activiti…ActivitiEntityEvent
TASK_CREATED 创建了新任务。它位于ENTITY_CREATE事件之后。当任务是由流程创建时, 这个事件会在TaskListener执行之前被执行。 org.activiti…ActivitiEntityEvent
TASK_COMPLETED 任务被完成了。它会在ENTITY_DELETE事件之前触发。当任务是流程一部分时,事件会在流程继续运行之前, 后续事件将是ACTIVITY_COMPLETE,对应着完成任务的节点。 org.activiti…ActivitiEntityEvent
TASK_TIMEOUT 任务已超时,在TIMER_FIRED事件之后,会触发用户任务的超时事件, 当这个任务分配了一个定时器的时候。 org.activiti…ActivitiEntityEvent
PROCESS_COMPLETED 流程已结束。在最后一个节点的ACTIVITY_COMPLETED事件之后触发。 当流程到达的状态,没有任何后续连线时, 流程就会结束。 org.activiti…ActivitiEntityEvent
MEMBERSHIP_CREATED 用户被添加到一个组里。事件包含了用户和组的id。 org.activiti…ActivitiMembershipEvent
MEMBERSHIP_DELETED 用户被从一个组中删除。事件包含了用户和组的id。 org.activiti…ActivitiMembershipEvent
MEMBERSHIPS_DELETED 所有成员被从一个组中删除。在成员删除之前触发这个事件,所以他们都是可以访问的。 因为性能方面的考虑,不会为每个成员触发单独的MEMBERSHIP_DELETED事件。 org.activiti…ActivitiMembershipEvent

引擎内部所有ENTITY_*事件都是与实体相关的。下面的列表展示了实体事件与实体的对应关系:

  • 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.
附加信息

只有同一个流程引擎中的事件会发送给对应的监听器。。的那个你有很多引擎 - 在同一个数据库运行 - 事件只会发送给注册到对应引擎的监听器。其他引擎发生的事件不会发送给这个监听器,无论实际上它们运行在同一个或不同的JVM中。

对应的事件类型(对应实体)都包含对应的实体。根据类型或事件,这些实体不能再进行更新(比如,当实例以被删除)。可能的话,使用事件提供的EngineServices来以安全的方式来操作引擎。即使如此,你需要小心的对事件对应的实体进行更新/操作。

没有对应历史的实体事件,因为它们都有运行阶段的对应实体。

MiCai wechat
扫一扫,关注微信订阅号
坚持原创技术分享,您的支持将鼓励我继续创作!