基于Activiti扩展的工作流引擎OpenWebFlow

开源地址:https://github.com/bluejoe2008/openwebflow(欢迎star)

1.    OpenWebFlow概述

OpenWebFlow是基于Activiti扩展的工作流引擎。Activiti (官方网站http://activiti.org/,代码托管在https://github.com/Activiti/Activiti)是一个新兴的基于 Apache 许可的支持 BPMN 2.0 标准的开源 BPM 产品,它是一个轻量级,可嵌入的 BPM 引擎,并且提供了功能丰富的开发和流程设计工具。OpenWebFlow与业务应用系统之间的关系如下图所示。

相对于Activiti,OpenWebFlow扩展的功能包括:

1) 完全接管了Activiti对活动(activity)权限的管理。
Activiti允许在设计model的时候指定每个活动的执行权限,但是,业务系统可能需要根据实际情况动态设置这些任务的执行权限(如:动态的Group)。OpenWebFlow完全实现了与流程定义时期的解耦,即用户对活动的访问控制信息单独管理(而不是在流程定义中预先写死),这样有利于动态调整权限,详见自定义活动权限管理;

2) 完全接管了Activiti对用户表(IDENTITY_XXX表)的管理。
在标准的工作流定义中,每个节点可以指定其候选人和候选用户组,但是比较惨的是,Activiti绑架了用户信息表的设计!这个是真正致命的,因为几乎每个业务系统都会属于自己的用户信息结构(包括User/Group/Membership),但不一定它存储在Activiti喜欢的那个库中,表的结构也不一定一样,有的时候,某些信息(如:动态的Group)压根儿就不采用表来存储。OpenWebFlow剥离了用户信息表的统一管理,客户程序可以忘掉Activiti的用户表、群组表、成员关系表,详见自定义用户成员关系管理;

3) 允许运行时定义activity!
彻底满足“中国特色”,并提供了安全的(同时也是优雅的)催办、代办、加签(包括前加签/后加签)、自由跳转(包括前进/后)、分裂节点等功能;

2.    快速上手

2.1    引入OpenWebFlow框架

2.1.1    以jar的方式引入OpenWebFlow

OpenWebFlow的发布形式是一组正常的jar,其中openwebflow-core.XXX.jar包含了核心的工作流控制模块,以及基于内存的管理器实现模块。

此外,OpenWebFlow还提供了几个jar:openwebflow-mgr-hibernate.XXX.jar,openwebflow-mgr-mybatis.XXX.jar,它们提供了管理器的SQL实现模块,分别选取hibernate和mybatis作为ORM模型。还有一个是openwebflow-mgr-test.XXX.jar,它包含了几个测试类。

最新版本的下载地址:

https://gitee.com/bluejoe/openwebflow/blob/master/openwebflow-core/target/openwebflow-core-0.9-SNAPSHOT.jar注意这些jar具有较多的依赖, https://gitee.com/bluejoe/openwebflow/tree/master/openwebflow-core/target/lib列举了所有的依赖包如下:

  • activation-1.1.jar
  • activiti-bpmn-converter-5.16.1.jar
  • activiti-bpmn-layout-5.16.1.jar
  • activiti-bpmn-model-5.16.1.jar
  • activiti-crystalball-5.16.1.jar
  • activiti-engine-5.16.1.jar
  • activiti-explorer-5.16.1.jar
  • activiti-image-generator-5.16.1.jar
  • activiti-json-converter-5.16.1.jar
  • activiti-process-validation-5.16.1.jar
  • activiti-simple-workflow-5.16.1.jar
  • activiti-spring-5.16.1.jar
  • aopalliance-1.0.jar
  • commons-collections-2.0.jar
  • commons-dbcp-1.4.jar
  • commons-email-1.2.jar
  • commons-io-2.4.jar
  • commons-lang-2.6.jar
  • commons-lang3-3.3.2.jar
  • commons-logging-1.1.1.jar
  • commons-pool-1.5.4.jar
  • dcharts-widget-0.10.0.jar
  • groovy-all-2.1.3.jar
  • h2-1.3.168.jar
  • hamcrest-core-1.3.jar
  • imgscalr-lib-4.2.jar
  • jackson-annotations-2.2.3.jar
  • jackson-core-2.2.3.jar
  • jackson-databind-2.2.3.jar
  • javaGeom-0.11.1.jar
  • jcl-over-slf4j-1.7.6.jar
  • jgraphx-1.10.4.1.jar
  • joda-time-2.1.jar
  • junit-4.12.jar
  • log4j-1.2.17.jar
  • mail-1.4.1.jar
  • mybatis-3.2.8.jar
  • mybatis-spring-1.2.2.jar
  • mysql-connector-java-5.1.32.jar
  • servlet-api-2.5.jar
  • slf4j-api-1.7.2.jar
  • slf4j-jdk14-1.7.2.jar
  • slf4j-log4j12-1.7.6.jar
  • spring-aop-3.2.4.RELEASE.jar
  • spring-beans-3.2.4.RELEASE.jar
  • spring-context-3.2.4.RELEASE.jar
  • spring-core-3.2.4.RELEASE.jar
  • spring-expression-3.2.4.RELEASE.jar
  • spring-jdbc-3.2.4.RELEASE.jar
  • spring-orm-3.2.4.RELEASE.jar
  • spring-tx-3.2.4.RELEASE.jar
  • spring-web-3.2.4.RELEASE.jar
  • spring-webmvc-3.2.4.RELEASE.jar
  • vaadin-6.8.8.jar

2.1.2    以maven的方式引入OpenWebFlow

以maven的方式引入OpenWebFlow比较简单,pom.xml中的依赖项写成:

<dependency>
     <groupId>org.openwebflow</groupId>
     <artifactId>openwebflow-core </artifactId>
     <version>0.9-SNAPSHOT</version>
</dependency>

在引入依赖项之前可能需要先在本地仓库中安装OpenWebFlow项目。具体操作是在eclipse中选择OpenWebFlow项目,【右键菜单】【Maven】【install】。

2.2    配置文件

准备SpringIoC配置文件,分别是settings.properties 、activiti.cfg.core.xml和activiti.cfg.mem.xml(或者是activiti.cfg.sql.XXX.xml):

  • settings.properties:公共属性设置
  • activiti.cfg.core.xml:用以配置工作流引擎的基本配置信息;
  • activiti.cfg.mem.xml:用以定义一些用以支持OpenWebFlow工作的manager,注意名字中的mem,它暗示着仅提供了那些manager的基于内存实现的版本,类似的配置文件还可以是activiti.cfg.sql.XXX.xml;

2.2.1    settings.properties

settings.properties文件是一个正常的属性文件,用以spring IOC文件加载。如下是一个属性文件的内容:

mail.host=smtp.bluejoe.cn
mail.port=25
[email protected]
mail.password=sdbsupport
[email protected]
model.dir=../models
alarm.mail.template=classpath:/alarm-template.txt
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.hbm2ddl.auto=none
activitidb.url=jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000
activitidb.driver=org.hDriver
activitidb.username=sa
activitidb.password=
owfdb.url=jdbc:mysql://localhost:3306/openwebflow?useUnicode=true&amp;characterEncoding=UTF-8
owfdb.driver=com.mysql.jdbc.Driver
owfdb.username=root
owfdb.password=1

各属性的含义如下:

属性名

示例值

含义

mail.host

smtp.bluejoe.cn

催办邮件发件服务器主机地址

mail.port

25

催办邮件发件服务器端口号

mail.username

[email protected]

催办邮件发件账号名

mail.password

sdbsupport

催办邮件发件账号密码

mail.from

[email protected]

催办邮件发件人

model.dir

../models

自动加载的BPMN模型路径

alarm.mail.template

classpath:/alarm-template.txt

催办邮件正文模板

hibernate.dialect

org.hibernate.dialect.MySQLDialect

Hibernate方言

hibernate.hbm2ddl.auto

none

Hibernate DDL设置

activitidb.url

jdbc:h2:mem:activiti;DB_CLOSE_DELAY

Activiti数据库JDBC URL

activitidb.driver

org.h2.Driver

Activiti数据库JDBC驱动

activitidb.username

sa

Activiti数据库账号名

activitidb.password

Activiti数据库账号密码

owfdb.url

jdbc:mysql://localhost:3306/openwebflow?useUnicode

OpenWebFlow数据库JDBC URL

owfdb.driver

com.mysql.jdbc.Driver

OpenWebFlow数据库JDBC驱动

owfdb.username

root

OpenWebFlow数据库账号名

owfdb.password

1

OpenWebFlow数据库账号密码

2.2.2    activiti.cfg.core.xml配置

activiti.cfg.core.xml的配置与Activiti要求的那个配置文件有点相似,但可以多一些内容,如下是个例子:

<!-- 工作流核心数据库配置 -->
<bean id="activitiDataSource" class="org.apache.commons.dbcp.BasicDataSource"
	destroy-method="close">
	<property name="driverClassName" value="${activitidb.driver}" />
	<property name="url" value="${activitidb.url}" />
	<property name="username" value="${activitidb.username}" />
	<property name="password" value="${activitidb.password}" />
	<property name="initialSize" value="20" />
	<property name="maxActive" value="50" />
	<property name="maxIdle" value="20" />
	<property name="minIdle" value="10" />
</bean>

<!-- 任务催办配置 -->
<bean id="myTaskAlarmService" class="org.openwebflow.alarm.impl.TaskAlarmServiceImpl">
	<!-- 截止日期提前量 -->
	<property name="periodInAdvance" value="P2D" />
	<!-- 设置消息通知机制 -->
	<property name="messageNotifier">
		<!-- 采用邮件发送 -->
		<bean class="org.openwebflow.alarm.impl.MailMessageNotifier">
			<property name="subjectTemplate" value="请尽快处理#{'$'}{task.name}任务" />
			<property name="messageTemplateResource" value="${alarm.mail.template}" />
			<property name="mailSender">
				<bean class="org.openwebflow.alarm.impl.MailSender">
					<property name="serverHost" value="${mail.host}" />
					<property name="serverPort" value="${mail.port}" />
					<property name="authUserName" value="${mail.username}" />
					<property name="authPassword" value="${mail.password}" />
					<property name="mailFrom" value="${mail.from}" />
				</bean>
			</property>
		</bean>
	</property>
	<property name="membershipManager" ref="myMembershipManager" />
	<property name="userDetailsManager" ref="myUserDetailsManager" />
	<property name="taskNotificationManager" ref="myTaskNotificationManager" />
</bean>

<bean id="transactionManager"
	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="activitiDataSource" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />

<!-- 配置对象 -->
<bean id="processEngineConfiguration" class="org.openwebflow.cfg.ProcessEngineConfigurationEx">
	<property name="dataSource" ref="activitiDataSource" />
	<property name="transactionManager" ref="transactionManager" />
	<property name="databaseSchemaUpdate" value="true" />
	<property name="jobExecutorActivate" value="false" />
	<property name="startEngineEventListeners">
		<list>
			<!-- 加载自定义表单元素类型 -->
			<bean class="org.openwebflow.cfg.LoadDummyFormTypes">
				<property name="typeNames" value="user" />
			</bean>
			<!-- 自定义成员关系管理 -->
			<bean class="org.openwebflow.cfg.ReplaceMembershipManager">
				<property name="customMembershipManager" ref="myMembershipManager" />
			</bean>
			<!-- 自定义活动权限管理 -->
			<bean class="org.openwebflow.cfg.ReplaceTaskAssignmentHandler">
				<!-- 授权处理器列表,会组成一个链,越靠后优先级越高(越靠外) -->
				<property name="handlers">
					<list>
						<!-- 自定义授权项列表 -->
						<bean
							class="org.openwebflow.assign.permission.ActivityPermissionAssignmentHandler">
							<property name="activityPermissionManager" ref="myActivityPermissionManager" />
						</bean>
						<!-- 允许授权代理 -->
						<bean
							class="org.openwebflow.assign.delegation.TaskDelagationAssignmentHandler">
							<property name="delegationManager" ref="myDelegationManager" />
							<property name="membershipManager" ref="myMembershipManager" />
							<property name="hideDelegated" value="false" />
						</bean>
					</list>
				</property>
			</bean>
			<!-- 自动导入流程模型 -->
			<bean class="org.openwebflow.cfg.ImportDefinedProcessModels">
				<property name="modelDir" value="${model.dir}" />
			</bean>
			<!-- 启动催办管理器 -->
			<bean class="org.openwebflow.cfg.StartTaskAlarmService">
				<property name="taskAlarmService" ref="myTaskAlarmService" />
				<property name="runOnStartup" value="false" />
			</bean>
			<!-- 加载自定义activity -->
			<bean class="org.openwebflow.cfg.LoadRuntimeActivityDefinitions">
				<property name="activityDefinitionManager" ref="myActivityDefinitionManager" />
			</bean>
		</list>
	</property>
</bean>

<!-- processEngine -->
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
	<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>

<!-- 工作流流转服务对象工厂 -->
<bean class="org.openwebflow.ctrl.impl.DefaultTaskFlowControlServiceFactory" />

<!-- processEngineTool -->
<bean id="processEngineTool" class="org.openwebflow.util.ProcessEngineTool" />

其中processEngineConfiguration是增强型的工作流引擎配置对象,可以设置自定义用户群组成员关系管理策略自定义活动权限管理策略等。

完整的例子参见:https://gitee.com/bluejoe/openwebflow/blob/master/openwebflow-test/src/test/resources/activiti.cfg.core.xml

2.2.3     activiti.cfg.mem.xml配置

在activiti.core.xml中会用到一些manager,activiti.mem.xml定义基于内存的manager实现,默认的activiti.mem.xml内容如下:

<!-- 自定义成员关系管理 -->
<bean id="myMembershipManager" class="org.openwebflow.mgr.mem.InMemoryMembershipManager" />
<bean id="myUserDetailsManager" class="org.openwebflow.mgr.mem.InMemoryUserDetailsManager" />

<!-- 自定义的活动权限表管理 -->
<bean id="myActivityPermissionManager"
	class="org.openwebflow.mgr.mem.InMemoryActivityPermissionManager" />
	
<!-- 代理关系管理 -->
<bean id="myDelegationManager" class="org.openwebflow.mgr.mem.InMemoryDelegationManager" />
	
<!-- 自定义的动态自定义活动管理 -->
<bean id="myActivityDefinitionManager"
	class="org.openwebflow.mgr.mem.InMemoryRuntimeActivityDefinitionManager" />

<bean id="myTaskNotificationManager" class="org.openwebflow.mgr.mem.InMemoryTaskNotificationManager" />

这里面定义了6个manager:

Manager类别

含义

myMembershipManager

自定义成员关系管理

myUserDetailsManager

自定义用户详细信息管理

myActivityPermissionManager

自定义的活动权限表管理

myDelegationManager

代理关系管理

myActivityDefinitionManager

自定义的动态自定义活动管理

myTaskNotificationManager

任务通知信息管理

完整的例子参见:https://gitee.com/bluejoe/openwebflow/blob/master/openwebflow-test/src/test/resources/activiti.cfg.mem.xml

与activiti.cfg.core.xml类似的可替代文件为activiti.cfg.sql.hibernate.xml和activiti.cfg.sql.mybatis.xml。

2.2.4    activiti.cfg.sql.hibernate.xml配置

activiti.sql.hibernate.xml提供基于SQL的manager实现,采用的ORM框架为Hibernate 4。

各manager的定义如下:

<!-- 代理记录管理 -->
<bean id="myDelegationManager"
	class="org.openwebflow.mgr.hibernate.service.SqlDelegationManager" />
<!-- 自定义成员关系管理 -->
<bean id="myMembershipManager"
	class="org.openwebflow.mgr.hibernate.service.SqlMembershipManager" />
<!-- 自定义用户表 -->
<bean id="myUserDetailsManager"
	class="org.openwebflow.mgr.hibernate.service.SqlUserDetailsManager" />
<!-- 自定义的活动权限表管理 -->
<bean id="myActivityPermissionManager"
	class="org.openwebflow.mgr.hibernate.service.SqlActivityPermissionManager" />
<!-- 自定义的动态自定义活动管理 -->
<bean id="myActivityDefinitionManager"
	class="org.openwebflow.mgr.hibernate.service.SqlRuntimeActivityDefinitionManager" />
<bean id="myTaskNotificationManager"
	class="org.openwebflow.mgr.hibernate.service.SqlTaskNotificationManager" />

此外,还需要定义数据源、Hibernate Session工厂,以及事务。

<!-- 数据库脚本见openwebflow.sql -->
<bean id="owfDataSource" class="org.apache.commons.dbcp.BasicDataSource"
	destroy-method="close">
	<property name="driverClassName" value="${owfdb.driver}" />
	<property name="url" value="${owfdb.url}" />
	<property name="username" value="${owfdb.username}" />
	<property name="password" value="${owfdb.password}" />
	<property name="initialSize" value="20" />
	<property name="maxActive" value="50" />
	<property name="maxIdle" value="20" />
	<property name="minIdle" value="10" />
</bean>

<!-- 配置SessionFactory -->
<bean id="sessionFactory"
	class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
	<property name="dataSource" ref="owfDataSource" />
	<property name="hibernateProperties">
		<props>
			<prop key="hibernate.dialect">${hibernate.dialect}</prop>
			<!-- <prop key="hibernate.dialect">org.hibernate.dialect.SQLServer2008Dialect</prop> -->
			<!-- 服务启动通过实体创建数据库表信息 -->
			<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
			<prop key="hibernate.show_sql">true</prop>
			<prop key="hibernate.format_sql">true</prop>
			<prop key="hibernate.jdbc.batch_size">20</prop>
			<prop key="hibernate.connection.release_mode">auto</prop>
			<prop key="hibernate.autoReconnect">false</prop>
			<prop key="hibernate.connection.autocommit">true</prop>
			<prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
			<prop key="hibernate.jdbc.use_streams_for_binary">true</prop>
			<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext
			</prop>
			<!--解决weblogic无法使用hql的问题 -->
			<prop key="hibernate.cglib.use_reflection_optimizer">true</prop>
		</props>
	</property>

	<!-- 自动扫描备注解的实体 -->
	<property name="packagesToScan">
		<list>
			<value>org.openwebflow.mgr.hibernate.entity</value>
		</list>
	</property>
</bean>

<!-- 配置一个事务管理器 -->
<bean id="transactionManager"
	class="org.springframework.orm.hibernate4.HibernateTransactionManager">
	<property name="sessionFactory" ref="sessionFactory" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />

采用HibernateORM,openwebflow-mgr-hibernate的代码结构如下:

其中,DAO类、实体类、服务类分别存放在dao、entity、service包下面。service类的事务声明以及entity的映射皆采取注解方式。

完整的例子参见:https://gitee.com/bluejoe/openwebflow/blob/master/openwebflow-test/src/test/resources/activiti.cfg.sql.hibernate.xml

2.2.5     activiti.cfg.sql.mybatis.xml配置

activiti.sql.mybatis.xml提供基于SQL的manager实现,采用的ORM框架为mybatis 3。

各manager的定义如下:

<!-- 代理记录管理 -->
<bean id="myDelegationManager"
	class="org.openwebflow.mgr.mybatis.service.SqlDelegationManager" />
<!-- 自定义成员关系管理 -->
<bean id="myMembershipManager"
	class="org.openwebflow.mgr.mybatis.service.SqlMembershipManager" />
<!-- 自定义用户表 -->
<bean id="myUserDetailsManager"
	class="org.openwebflow.mgr.mybatis.service.SqlUserDetailsManager" />
<!-- 自定义的活动权限表管理 -->
<bean id="myActivityPermissionManager"
	class="org.openwebflow.mgr.mybatis.service.SqlActivityPermissionManager" />
<!-- 自定义的动态自定义活动管理 -->
<bean id="myActivityDefinitionManager"
	class="org.openwebflow.mgr.mybatis.service.SqlRuntimeActivityDefinitionManager" />
<bean id="myTaskNotificationManager"
	class="org.openwebflow.mgr.mybatis.service.SqlTaskNotificationManager" />

此外,还需要定义数据源、SqlSessionFactory,以及事务。

<!-- 数据库脚本见openwebflow.sql -->
<bean id="owfDataSource" class="org.apache.commons.dbcp.BasicDataSource"
	destroy-method="close">
	<property name="driverClassName" value="${owfdb.driver}" />
	<property name="url" value="${owfdb.url}" />
	<property name="username" value="${owfdb.username}" />
	<property name="password" value="${owfdb.password}" />
	<property name="initialSize" value="20" />
	<property name="maxActive" value="50" />
	<property name="maxIdle" value="20" />
	<property name="minIdle" value="10" />
</bean>

<!-- 创建SqlSessionFactory,同时指定数据源-->
<bean id="owlSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="owfDataSource" />
</bean>

<!-- 配置一个事务管理器 -->
<bean id="transactionManager"
	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="owfDataSource" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />

采用Mybatis ORM,openwebflow-mgr-mybatis的代码结构如下:

其中,Mapper接口、实体类、服务类分别存放在mapper、entity、service包下面。service类的事务声明以及Mapper的映射皆采取注解方式。

完整的例子参见:https://gitee.com/bluejoe/openwebflow/blob/master/openwebflow-test/src/test/resources/activiti.cfg.sql.mybatis.xml

2.3    数据库设计

首先,Activiti引擎本身需要用到一系列的数据表,设置好数据源后,Activiti会自动生成这些表。

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

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

OpenWebFlow为一系列manager提供了基于数据库的实现,需要用到一些数据表,对应的建库脚本参见https://gitee.com/bluejoe/openwebflow/tree/master/doc目录下的:

  • openwebflow-mysql4.sql:MySQL4脚本
  • openwebflow-mysql5.sql:MySQL5脚本
  • openwebflow-sqlserver2008.sql:SQLServer2008脚本
  • openwebflow-oracle10g.sql:Oracle脚本

其中共定义了6张表格:

  • OWF_ACTIVITY_CREATION:用以存储自定义的活动定义信息
  • OWF_ACTIVITY_PERMISSION:用以存储自定义的活动权限信息
  • OWF_DELEGATION:用以存储用户代理信息
  • OWF_NOTIFICATION:用以存储催办通知记录
  • OWF_MEMBERSHIP:用以存储用户组成员关系
  • OWF_USER:用以存储用户信息

注意:OWF_MEMBERSHIP和OWF_USER仅为测试使用,建议用户使用自己的数据表(OpenWebFlow本身努力的一个方向就是将用户及成员关系管理与工作流引擎剥离开),并包装自己的Manager。

2.4    使用ApplicationContext定义的bean

采用Spring IoC框架加载完XML配置文件之后,ApplicationContext中会包含如下变量,可供客户程序使用:

  • processEngine:工作流引擎对象,标准的Activiti对象
  • processEngineTool:针对processEngine提供了一些工具方法
  • defaultTaskFlowControlServiceFactory:任务流控制器的工厂对象
  • repositoryService:提供了管理和控制发布包和流程定义的操作
  • RuntimeService:负责启动一个流程定义的新实例
  • TaskService:与任务相关相关的操作
  • IdentityService:管理(创建,更新,删除,查询...)群组和用户
  • FormService:提供了启动表单和任务表单两个概念
  • HistoryService:提供了Activiti引擎手机的所有历史数据
  • ManagementService:可以查询数据库的表和表的元数据

如下是使用OpenWebFlow的示例代码,可以看出来与Activiti的用法完全一致:


ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:activiti.cfg.mem.xml");  
ProcessEngineTool tool = ctx.getBean(ProcessEngineTool.class);  
ProcessEngine processEngine = tool.getProcessEngine();  
// 启动流程实例  
ProcessInstance instance = processEngine.getRuntimeService().startProcessInstanceByKey("test1");  
TaskService taskService = processEngine.getTaskService();  
//会自动跳转到第一个task  
//management可以访问该task  
Assert.assertEquals(1, taskService.createTaskQuery().taskCandidateGroup("management").count());  

2.5    运行测试用例

在openwebflow-test项目源代码中,用户可以找到一组测试用例来对工作流引擎的功能进行测试,它们分别是:

  • MemProcessEngineTest:基于内存的manager测试
  • SqlHibernateProcessEngineTest:基于hibernateORM的manager测试
  • SqlMybatisProcessEngineTest:基于mybatisORM的manager测试

以上3个测试类都继承于AbstractProcessEngineTest:

AbstractProcessEngineTest提供了测试方法:

方法摘要

void

testActivityPermission() 
          
测试流程动态授权

 void

testAlarm() 
          
测试催办功能

 void

testCachedDefinitions() 
          
测试TaskDefinition

 void

testDelegation() 
          
测试代理功能

 void

testInsertTasksAfter() 
          
测试后加签

 void

testInsertTasksBefore() 
          
测试前加签

 void

testInsertTasksWithPersistence() 
          
测试加签功能的持久化

 void

testModelDeployment() 
          
测试流程模型部署

 void

testMove() 
          
测试自由跳转

 void

testMultiInstancesLoop() 
          
测试多实例节点

 void

testSplit() 
          
测试测试节点分裂

其中为了配合测试设计了一个复杂的流程(models/test2.bpmn)如图所示:

选择指定的测试单元(如:MemProcessEngineTest),以“JUnit测试”的方式运行(Run As…),即可观察到测试结果:

也可以选择运行测试套件AllTests:

3.    熟悉OpenWebFlow代码

3.1    下载源码

用户可以下载OpenWebFlow的zip包,下载地址为:https://gitee.com/bluejoe/openwebflow/archive/master.zip

也可以通过git方式获取到最新源码,git资源库地址为:https://gitee.com/bluejoe/openwebflow.git

3.2    代码结构

OpenWebFlow源码包含5个maven工程,其中openwebflow工程是父工程,它声明了包含openwebflow-core、openwebflow-mgr-hibernate、openwebflow-mgr-mybatis、openwebflow-test等4个model。

  • openwebflow-core:核心工程,包含OpenWebFlow扩展引擎的所有核心内容、以及基于内存的manager实现。
  • openwebflow-mgr-hibernate:依赖于openwebflow-core,提供了基于数据库的manager实现,ORM框架采用Hibernate。
  • openwebflow-mgr-mybatis:依赖于openwebflow-core,提供了基于数据库的manager实现,ORM框架采用MyBatis。
  • openwebflow-test:依赖于以上项目,提供了测试用例,包括配置文件、测试类等。

3.3    build项目

获取到的项目源码可以采用Maven完成build和install,如下为build截图:

3.4    核心对象

3.4.1    ProcessEngineConfigurationEx

ProcessEngineConfigurationEx是针对Activiti提供的ProcessEngineConfiguration类的派生类:

二者大部分参数完全一致,唯一不同的是,ProcessEngineConfigurationEx提供了一个属性:startEngineEventListeners。

startEngineEventListeners用以定义工作流引擎启动的时候需要同时启动的其它任务,startEngineEventListeners是个List,因此可以随意增加新的任务,默认的core.xml中会加载如下任务:LoadDummyFormTypes、ReplaceMembershipManager、ReplaceTaskAssignmentHandler、ImportDefinedProcessModels、StartTaskAlarmService、LoadRuntimeActivityDefinitions。各任务及类属性列表如下:

任务类

用途

属性名

属性含义

LoadDummyFormTypes

加载一些无用的Form类型,用以屏蔽一些自定义Form带来的错误

typeNames

需要屏蔽的Form类型名,以;分隔,如:user

ReplaceMembershipManager

直接接管用户组成员关系

customMembershipManager

指定客户程序自定义的管理器

ReplaceTaskAssignmentHandler

接管Activiti的用户权限管理,如果你想实现动态的节点权限分配,那必须要打开它。

handlers

定义一个授权处理器列表,值类型为List,运行时刻各授权处理器列表会组成一个链,越靠后优先级越高(越靠外)

ImportDefinedProcessModels

自动从指定目录导入BPMN模型

modelDir

用以指定模型的路径,可以是classpath:等路径

StartTaskAlarmService

启动任务催办服务

taskAlarmService

设置催办服务对象

runOnStartup

是否一开始就启动(默认为true)

LoadRuntimeActivityDefinitions

加载运行时的节点定义,主要用来支持在运行时刻定义新的节点

activityDefinitionManager

指定节点定义管理器

3.4.2    ProcessEngineTool

ProcessEngineTool提供了一些工具方法,这些方法的功能一般很难通过ProcessEngine直接拿到:

方法摘要

 org.activiti.engine.repository.Model

createNewModel(java.lang.String name, java.lang.String description) 
          
创建一个空白的Model对象

 org.activiti.engine.repository.Deployment

deployModel(java.lang.String modelId) 
          
部署一个已注册的model

 org.activiti.engine.impl.pvm.process.ActivityImpl

getActivity(java.lang.String processDefId, java.lang.String activityId) 
          
获取指定名字的活动

 java.util.Map<java.lang.String,java.lang.Object>

getHistoricProcessVariables(java.lang.String processId) 
          
获取指定历史流程的变量列表

 org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity

getProcessDefinition(java.lang.String processDefId) 
          
获取指定ID的流程定义

 org.activiti.engine.ProcessEngine

getProcessEngine() 
           

 void

grantPermission(org.activiti.engine.impl.pvm.process.ActivityImpl activity, java.lang.String assigneeExpression, java.lang.String candidateGroupIdExpressions, java.lang.String candidateUserIdExpressions) 
          
设置指定活动的用户权限,包括钦定用户、候选用户、候选组

 void

grantPermission(java.lang.String processDefId, java.lang.String activityId, java.lang.String assigneeExpression, java.lang.String candidateGroupIdExpressions, java.lang.String candidateUserIdExpressions) 
          
设置指定活动的用户权限,包括钦定用户、候选用户、候选组

 void

setProcessEngine(org.activiti.engine.ProcessEngine processEngine) 

3.4.3    各种Utils

OpenWebFlow提供了一些常见的工具类,如下所示:

类摘要

CloneUtils

实现对象的克隆功能

ExpressionUtils

实现常见类型的expression的包装和转换

IdentityUtils

实现用户、成员关系等相关操作

ModelUtils

包装了对BPMN模型的部署、注册等功能

ProcessDefinitionUtils

流程定义相关操作的封装

3.4.4    TaskFlowControlService

TaskFlowControlService用以实现流程的自由控制,它提供的方法如下:

方法摘要

 org.activiti.engine.impl.pvm.process.ActivityImpl[]

insertTasksAfter(java.lang.String targetTaskDefinitionKey, java.lang.String... assignees) 
          
后加签

 org.activiti.engine.impl.pvm.process.ActivityImpl[]

insertTasksBefore(java.lang.String targetTaskDefinitionKey, java.lang.String... assignees) 
          
前加签

 void

moveBack() 
          
后退一步

 void

moveBack(org.activiti.engine.impl.persistence.entity.TaskEntity currentTaskEntity) 
          
后退至指定活动

 void

moveForward() 
          
前进一步

 void

moveForward(org.activiti.engine.impl.persistence.entity.TaskEntity currentTaskEntity) 
          
前进至指定活动

 void

moveTo(java.lang.String targetTaskDefinitionKey) 
          
跳转(包括回退和向前)至指定活动节点

 void

moveTo(java.lang.String currentTaskId, java.lang.String targetTaskDefinitionKey) 
          
跳转(包括回退和向前)至指定活动节点

 void

moveTo(org.activiti.engine.impl.persistence.entity.TaskEntity currentTaskEntity, java.lang.String targetTaskDefinitionKey) 
          
跳转(包括回退和向前)至指定活动节点

 org.activiti.engine.impl.pvm.process.ActivityImpl

split(java.lang.String targetTaskDefinitionKey, boolean isSequential, java.lang.String... assignees) 
          
分裂某节点为多实例节点

 org.activiti.engine.impl.pvm.process.ActivityImpl

split(java.lang.String targetTaskDefinitionKey, java.lang.String... assignee) 
          
分裂某节点为多实例节点

TaskFlowControlService需要一个TaskFlowControlServiceFactory来创建,可以从applicationcontext中获取到该工厂对象。

4.    核心功能的设计与使用【略】

5.    使用管理器接口实现自定义扩展

OpenWebFlow需要用户提供6类管理器的接口,它们分别是:

  • RuntimeActivityDefinitionManager:负责获取活动的定义信息,用以支持运行时期的新建活动
  • ActivityPermissionManager:负责获取活动的权限设置信息
  • TaskNotificationManager:负责存取任务催办通知信息
  • DelegationManager:负责获取用户的代理信息
  • UserDetailsManager:负责获取用户的信息(包括E-mail、昵称、手机号等),主要用以发送催办通知
  • IdentityMembershipManager:负责获取用户组成员关系,获取某用户的候选任务队列时,需要通过用户名获取到用户组

除了这些Manager之外,用户会发现OpenWebFlow还提供了一系列的ManagerEx接口:

  • ActivityPermissionManagerEx:负责保存活动的权限设置信息
  • TaskNotificationManagerEx:负责保存任务催办通知信息
  • DelegationManagerEx:负责保存用户的代理信息
  • UserDetailsManagerEx:负责保存用户的信息
  • IdentityMembershipManagerEx:负责保存用户组成员关系

可以简单的认为,Manager接口主要用以信息读取(read),ManagerEx接口主要用以信息写入(write),注意使用OpenWebFlow引擎时,ManagerEx不是必须要提供相应实现的!OpenWebFlow引擎的所有操作只会调用到Manager而非ManagerEx,提供ManagerEx的唯一用处是为了测试(如果没有写入,读取返回的永远是空白,测试就无法正常进行了)。

5.1   活动定义管理

客户程序往往需要在运行时候调整某个工作流的流程,如:让活动step5执行完之后跳转至step2,这样的操作需要创建一个新的路径,为了保证后续流程的正常执行(特别是应用重启之后),这样的路径需要保存和加载。

5.1.1    RuntimeActivityDefinitionManager

RuntimeActivityDefinitionManager包含如下方法:

方法摘要

java.util.List <RuntimeActivityDefinitionEntity>

list() 
          获取所有的活动定义信息,引擎会在启动的时候加载这些活动定义并进行注册

 void

removeAll() 
          删除所有活动定义

 void

save(RuntimeActivityDefinitionEntity entity) 
          新增一条活动定义的信息

5.1.2    RuntimeActivityDefinitionEntity

RuntimeActivityDefinitionEntity对应于一条活动的定义信息:

方法摘要

 void

deserializeProperties() 
          
反序列化PropertiesText到Map

 java.lang.String

getFactoryName() 
          
获取工厂名

 java.lang.String

getProcessDefinitionId() 
          
获取流程定义的ID

 java.lang.String

getProcessInstanceId() 
          
获取流程实例的ID

 java.lang.String

getPropertiesText() 
          
获取PropertiesText,它是一个JSON字符串

<T> T

getProperty(java.lang.String name) 
          
获取指定的属性值

 void

serializeProperties() 
          
序列化Map至PropertiesText

 void

setFactoryName(java.lang.String factoryName) 
          
设置工厂名

 void

setProcessDefinitionId(java.lang.String processDefinitionId) 
          
设置流程定义ID

 void

setProcessInstanceId(java.lang.String processInstanceId) 
          
设置流程实例ID

 void

setPropertiesText(java.lang.String propertiesText) 
          
设置PropertiesText

<T> void

setProperty(java.lang.String name, T value) 

5.2    活动权限管理

5.2.1    ActivityPermissionManager

活动权限的管理接口为ActivityPermissionManager,它的定义如下:

方法摘要

 ActivityPermissionEntity

load(java.lang.String processDefinitionId, java.lang.String taskDefinitionKey, boolean addOrRemove) 
          
获取指定活动的权限定义信息

5.2.2    ActivityPermissionEntity

非常简单,它只需要一个方法,该方法返回一个ActivityPermissionEntity,该实体的定义如下:

方法摘要

 java.lang.String

getAssignee() 
          
获取直接授权人

 java.lang.String[]

getGrantedGroupIds() 
          
获取候选组列表

 java.lang.String[]

getGrantedUserIds() 
          
获取候选用户列表

5.2.3    ActivityPermissionManagerEx

ActivityPermissionManagerEx实现对活动权限表的“写”操作:

方法摘要

void

removeAll() 
          
删除所有权限定义信息

 void

save(java.lang.String processDefId, java.lang.String taskDefinitionKey, java.lang.String assignee, java.lang.String[] candidateGroupIds, java.lang.String[] candidateUserIds) 
          
保存一条权限定义信息

5.3    任务催办通知管理

5.3.1    TaskNotificationManager

TaskNotificationManager负责读取和设置任务催办的状态,该接口也很简单:

方法摘要

 boolean

isNotified(java.lang.String taskId) 
          
判断指定任务是否通知过

 void

setNotified(java.lang.String taskId) 
          
设置指定任务的通知状态

可以理解成TaskNotificationManager维护了一个队列,记录了每个任务的通知状态(已通知为true,未通知为false)。

5.3.2    TaskNotificationManagerEx

TaskNotificationManagerEx实现了对通知记录的“写”操作:

方法摘要

 void

removeAll() 
          
删除所有通知记录

5.4    用户代理关系管理

5.4.1    DelegationManager

DelegationManager用以维护用户之间的代理关系。接口包含2个方法:

方法摘要

 java.lang.String[]

getDelegates(java.lang.String delegated) 
          
获取指定用户的代理人列表

 java.util.List<DelegationEntity>

listDelegationEntities() 
          
获取所有的代理信息列表,引擎会在启动的时候加载

5.4.2    DelegationEntity

DelegationEntity描述一条代理关系:

方法摘要

 java.lang.String

getDelegate() 
          
获取当前代理记录的代理人

 java.lang.String

getDelegated() 
          
获取当前代理记录的被代理人

5.4.3    DelegationManagerEx

DelegationManagerEx用以实现对代理记录的“写”操作:

方法摘要

 void

removeAll() 
          
删除所有代理信息

 void

saveDelegation(java.lang.String delegated, java.lang.String delegate) 
          
保存一条代理信息

5.5   用户详细信息管理

5.5.1    UserDetailsManager

UserDetailsManager负责获取用户的信息(包括E-mail、昵称、手机号等),主要用以发送催办通知。UserDetailsManager接口包含的方法如下:

方法摘要

 UserDetailsEntity

findUserDetails(java.lang.String userId) 
          
根据用户名获取用户详细信息

5.5.2    UserDetailsEntity

UserDetailsEntity用以描述用户详细信息,注意它没有强制要求提供诸如getName()这样的方法,而是提供了一个getProperty(Stringname)方法:

方法摘要

<T> T

getProperty(java.lang.String name) 
          
获取指定属性的值

 java.lang.String[]

getPropertyNames() 
          
获取所有的属性名

 java.lang.String

getUserId() 
          
获取用户的ID

<T> void

setProperty(java.lang.String name, T value) 
          
设置指定属性的值

UserDetailsEntity同时提供了几个字符串常量:

字段摘要

static java.lang.String

STRING_PROPERTY_EMAIL 
          EMAIL
属性名

static java.lang.String

STRING_PROPERTY_MOBILE_PHONE_NUMBER 
          
手机号码属性名

static java.lang.String

STRING_PROPERTY_NICK_NAME 
          
昵称属性名

static java.lang.String

STRING_PROPERTY_USER_ID 
          
用户ID属性名

5.5.3    UserDetailsManagerEx

UserDetailsManagerEx用以实现用户信息的“写”操作:

方法摘要

 void

removeAll() 
          
删除所有用户信息

 void

saveUserDetails(UserDetailsEntity userDetails) 
          
保存某个用户的信息

5.6   用户组成员关系管理

5.6.1    IdentityMembershipManager

IdentityMembershipManager负责获取用户组成员关系,简单点,就是获取指定用户所在的组的ID列表,以及指定组内的成员ID列表。

方法摘要

java.util.List <java.lang.String>

findGroupIdsByUser(java.lang.String userId) 
          
获取指定的用户所在的组ID列表

 java.util.List <java.lang.String>

findUserIdsByGroup(java.lang.String groupId) 
          
获取指定组的成员用户ID列表

5.6.2    IdentityMembershipManagerEx

IdentityMembershipManagerEx提供了对成员关系信息的“写”操作:

方法摘要

 void

removeAll() 
          
删除所有成员关系

 void

saveMembership(java.lang.String userId, java.lang.String groupId) 
          
保存成员关系

6.    其他帮助

本文档的下载地址为:https://gitee.com/bluejoe/openwebflow/tree/master/doc,可通过该地址及时查阅最新版本。

如果用户需要查阅OpenWebFlow的Java API,可以参考javadoc(https://gitee.com/bluejoe/openwebflow/tree/master/openwebflow-core/doc/javadoc)。

另外可以关注Wiki(https://gitee.com/bluejoe/openwebflow/wiki),提交话题(https://gitee.com/bluejoe/openwebflow/issues),以及与作者[email protected]直接联系。

7.    Activiti的BUG及对策

目前作者发现Activiti框架存在2个bug,主要表现在:

第一个bug是在BaseBpmnJsonConverter将BPMN模型转存为JSON格式的时候,会忽略对true布尔值的输出,这个bug会造成JsonConverterUtil.getPropertyValueAsBoolean()获取到false值(因为此时的判断标准变成Yes或No)。该bug的报告地址:https://github.com/Activiti/Activiti/pull/464#event-204722250

另外一个bug是在BPMN文件加载的时候,当本地字符集为非UTF-8(如:GB2312)时,会出现模型加载的错误。该bug的报告地址:https://github.com/Activiti/Activiti/pull/486#event-220121880

目前(2014年)作者已经针对以上两个bug提交了bugfix并被master版本合并。不过考虑到版本稳定性问题,OpenWebFlow最新版本还是采用了其他方法来避免了如上bug的发生。

猜你喜欢

转载自my.oschina.net/bluejoe/blog/1552284