论坛首页 Java企业应用论坛

开源工作流引擎activiti与bboss整合使用方法浅析

浏览 8113 次
精华帖 (0) :: 良好帖 (2) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-05-01   最后修改:2013-03-26
本文介绍开源工作流引擎activiti与bboss ioc框架整合方法,涉及内容如下:
a).activiti采用bboss ioc来配置和初始化流程引擎
b).在activiti流程中使用bboss ioc容器托管的组件
c).bboss 托管activiti引擎流程处理事务

下面介绍前两部分,事务部分请参考文章《bboss持久层事务管理组件托管第三方持久层框架(mybatis等)事务功能介绍 》

1 activiti-bboss源码工程下载及构建方法
1.1 环境准备
安装好jdk 1.6,ant 1.7.1或以上版本,并配置好jdk和ant的环境变量
安装好mysql数据库(自行安装),并启动
在mysql中建立一个activiti的数据库
准备好eclipse

1.2 下载activiti与bboss结合的源码工程
基于activiti-5.12和最新的bbossgroups 3.6.2
http://www.bbossgroups.com/file/download.htm?fileName=activiti-bboss-5.12.zip

1.3 下载后解压并构建

执行解压目录下的run.bat文件构建activiti流程引擎的jar包,生成的jar存放在distrib目录下:



2 在源码工程中运行流程测试用例
解压目录是一个eclipse工程,将其导入eclipse中
2.1 修改src/test/resources/poolman.xml中的数据库地址为mysql数据库地址,并设置账户:
 
......
<dbname>mysql</dbname>
<driver>com.mysql.jdbc.Driver</driver>

     <url>jdbc:mysql://localhost:3306/activiti</url> 

    <username>root</username>
    <password>123456</password>
............


2.2 修改src/test/resources/activiti.cfg.xml文件内容
为activiti配置bboss数据源mysql(是刚才在poolman.xml文件中配置的dbname为mysql的数据源的名称,该名称可以根据自己需要进行命名,没有特殊约定):

....
<property name="dataSource" factory-class="com.frameworkset.common.poolman.util.SQLManager" factory-method="getTXDatasourceByDBName">
    	<construction>
    		<property value="mysql" />
    	</construction>
    </property>
.....


为activiti配置bboss ioc容器对象,以便在测试用例中引用bboss ioc管理的组件来获取和设置流程环节处理人:
<property name="beanFactory" factory-class="org.frameworkset.spi.DefaultApplicationContext" 
			  factory-method="getApplicationContext">
			  <construction>
			  <property value="beans.xml"/>
			  </construction>
	</property>

被activiti使用的bboss ioc容器可以是独立的ioc容器,例如如上配置就是一个加载beans.xml的类型为DefaultApplicationContext的独立ioc容器(本文以独立ioc容器为例);也可以是bboss mvc对应的ioc容器,这样web应用中的流程就可以使用mvc ioc容器中配置的各种业务组件,mvc容器配置示例如下:
<property name="beanFactory" 
			  factory-class="org.frameworkset.web.servlet.support.WebApplicationContextUtils" 
			  factory-method="getWebApplicationContext"/>


下面是一个完整的activiti.cfg.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<properties>
  <property name="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
     <property name="dataSource" factory-class="com.frameworkset.common.poolman.util.SQLManager" factory-method="getTXDatasourceByDBName">
    	<construction>
    		<property value="mysql" />
    	</construction>
    </property>
    <!-- Database configurations -->
    <property name="databaseSchemaUpdate" value="true" />    
    <!-- job executor configurations -->
    <property name="jobExecutorActivate" value="false" />    
    <!-- mail server configurations -->
    <property name="mailServerPort" value="5025" />    
    <property name="history" value="full" />
  </property>
  <property name="beanFactory" factory-class="org.frameworkset.spi.DefaultApplicationContext" 
			  factory-method="getApplicationContext">
			  <construction>
			  <property value="beans.xml"/>
			  </construction>
	</property>
</properties>

加载activiti.cfg.xml并启动Activiti:
TransactionManager tm = new TransactionManager();
		try
		{
			tm.begin();
			processEngine = ProcessEngineConfiguration
					.createProcessEngineConfigurationFromResource(xmlPath)
					.buildProcessEngine();
			tm.commit();
		}
		catch(Exception e)
		{
			throw new RuntimeException(e);
		}
		finally
		{
			tm.releasenolog();
		}

说明:之所以将初始化activiti流程引擎的逻辑包含在一个bboss 的事务上下文之中,是因为activiti使用的是mybatis 3.0.6版本存在一个缺陷,这个缺陷表现为:先通过sqlsession开启一个事务,然后直接通过sqlsession的getconnection方法获取一个DB链接来执行多次数据库的增删改操作,如果全部执行成功,调用sqlsession的commit方法提交事务,如果执行成功一部分操作后出现异常,再调用sqlsession的rollback回滚事务,这样问题就出来了,mybatis会直接把connection返回给链接池,并不会提交或者回滚之前的db操作(因为这些操作没有调用sqlsession的相关方法来执行,而是直接通过connection完成的),connection并没有被物理关闭而只是返回到池中,这样就导致了之前修改和删除的数据被锁定了,导致后续针对这些锁定的记录的修改操作全部被阻塞造成不可预估的后果。

activiti在启动时会从mybatis的sqlsession中直接获取一个connection来执行创建和更新流程的库表结构并往属性表中插入或者更新流程的版本信息,如果在执行的过程中出现了问题的话,会导致数据库死锁,但是我们采用bboss的事务管理框架来托管mybatis的事务后就能够正常解决这个问题。

2.3 beans.xml 配置和管理bboss ioc组件

我们在src/test/resources/beans.xml文件中配置了一个组件:
<properties>
<property name="taskAssigneeAssignment" class="org.frameworkset.activiti.test.AssigneeAssignment"/>

</properties>


org.frameworkset.activiti.test.AssigneeAssignment组件是一个activiti task listener ,同时有个getHandlerMan方法用来获取任务处理人,代码如下:
package org.frameworkset.activiti.test;

import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;


/**
 * @author biaoping.yin
 */
public class AssigneeAssignment implements TaskListener {

  public void notify(DelegateTask delegateTask) {
    delegateTask.setAssignee("kermit");//设置任务处理人
  }
  
  public String getHandlerMan()
  {
	  return "kermit";
  }
  
}


3 在activiti的流程定义文件中引用bboss ioc中的组件
3.1 组件作为任务监听器使用
流程定义文件src/test/resources/org/activiti/examples/bpmn/tasklistener/CustomTaskAssignmentTest.testAssigneeAssignment.bpmn20.xml相关内容:
<activiti:taskListener event="create" delegateExpression="${taskAssigneeAssignment}" />


流程定义文件完整内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<definitions id="taskListenerExample" 
  xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
  xmlns:activiti="http://activiti.org/bpmn"
	targetNamespace="Examples">
	
	<process id="setAssigneeInListener" name="Custom task assignment Example">
	
		<startEvent id="theStart" />
		
		<sequenceFlow id="flow1" sourceRef="theStart" targetRef="task1" />

		<userTask id="task1" name="Schedule meeting" >
		  <documentation>
		    Schedule a meeting with management.
		  </documentation>
		  <extensionElements>
		    <!--<activiti:taskListener event="create" class="org.activiti.examples.bpmn.tasklistener.AssigneeAssignment" />  -->
		    <activiti:taskListener event="create" delegateExpression="${taskAssigneeAssignment}" />
		  </extensionElements>
		</userTask>
		
		<sequenceFlow id="flow2" sourceRef="task1" targetRef="theEnd" />
		
		<endEvent id="theEnd" />
		
	</process>

</definitions>


流程执行示例
测试用例:
src/test/java/org/activiti/examples/bpmn/tasklistener/CustomTaskAssignmentTest.java
测试方法:
 public void testAssigneeAssignment() {
//启动流程
    runtimeService.startProcessInstanceByKey("setAssigneeInListener");
//通过任务监听器组件taskAssigneeAssignment以下方法指定了流程环节处理人为kermit,所以kermit会有一个待办任务
//public void notify(DelegateTask delegateTask) {
//    delegateTask.setAssignee("kermit");//设置任务处理人
//  }

    
assertNotNull(taskService.createTaskQuery().taskAssignee("kermit").singleResult());
    assertEquals(0, taskService.createTaskQuery().taskAssignee("fozzie").count());
    assertEquals(0, taskService.createTaskQuery().taskAssignee("gonzo").count());
  }

3.2 直接调用组件方法获取任务处理人
流程定义文件src/test/resources/org/activiti/examples/bpmn/usertask/taskassignee/TaskAssigneeTest.testTaskAssignee.bpmn20.xml相关内容:
<formalExpression>${taskAssigneeAssignment.getHandlerMan()}</formalExpression>


完整流程定义文件:
<?xml version="1.0" encoding="UTF-8"?>
<definitions id="taskAssigneeExample" 
  xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
  xmlns:activiti="http://activiti.org/bpmn"
  targetNamespace="Examples">
  
  <process id="taskAssigneeExampleProcess" name="Schedule meeting reminder">
  
    <startEvent id="theStart" />
    
    <sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" />

    <userTask id="theTask" name="Schedule meeting" >
      <documentation>
        Schedule an engineering meeting for next week with the new hire.
      </documentation>
      <humanPerformer>
        <resourceAssignmentExpression>
<!--           <formalExpression>kermit</formalExpression> -->
 <formalExpression>${taskAssigneeAssignment.getHandlerMan()}</formalExpression>
        </resourceAssignmentExpression>
      </humanPerformer>
    </userTask>
    
    <sequenceFlow id="flow2" sourceRef="theTask" targetRef="theEnd" />
    
    <endEvent id="theEnd" />
    
  </process>

</definitions>

流程执行:
测试用例src/test/java/org/activiti/examples/bpmn/usertask/taskassignee/TaskAssigneeTest.java

测试方法:
 public void testTaskAssignee() {    
    
    // Start process instance
	TransactionManager tm = new TransactionManager();
	try {
		tm.begin();//start bboss transaction
	    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("taskAssigneeExampleProcess");
	    
	    // Get task list
//流程中通过${taskAssigneeAssignment.getHandlerMan()}表达式调用组件方法指定了kermit为任务处理人
	    List<Task> tasks = taskService
	      .createTaskQuery()
	      .taskAssignee("kermit")
	      .list();
	    assertEquals(1, tasks.size());
	    Task myTask = tasks.get(0);
	    assertEquals("Schedule meeting", myTask.getName());
	    assertEquals("Schedule an engineering meeting for next week with the new hire.", myTask.getDescription());
	
	    // Complete task. Process is now finished
	    taskService.complete(myTask.getId());
	    // assert if the process instance completed
	    assertProcessEnded(processInstance.getId());
	    tm.commit();//commit bboss transaction

	} catch (Throwable e) {
		try {
			tm.rollback();//rollback bboss transaction
		} catch (RollbackException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
  }

4.activiti和bboss整合相关资源说明
本节说明bboss和activiti整合时,需要用到bboss和activiti的哪些jar包、运行时依赖的jar包以及相关配置文件,见下表:



5.activiti-bboss eclipse demo工程
为了更好地方便大家使用activiti-bboss的整合版本,特意整理了一个demo工程,工程下载地址:
http://www.bbossgroups.com/file/download.htm?fileName=activiti-bboss-example.zip

工程里面已经根据上文方法做好相关配置
activiti.cfg.xml
beans.xml
poolman.xml

下载解压后将其导入eclipse,修改poolman.xml中的数据库连接地址和账号即可运行demo工程中的测试用例:
/activiti-bboss-example/src/org/frameworkset/activiti/test/SimpleActivitTest.java
用例方法为:
@Test
	public void runBare() throws Throwable {

		initializeProcessEngine();// 初始化流程引擎,默认加载activiti.cfg.xml文件

		initializeServices();// 初始化流程相关服务

		initusers();// 初始化测试用户

		String deploymentId = null;

		try {

			deploymentId = annotationDeploymentSetUp(processEngine);// 部署测试流程

			testAssigneeAssignment();// 执行测试用例
			testTaskAssignee();// 执行测试用例

		} catch (AssertionFailedError e) {

			throw e;

		} catch (Throwable e) {

			throw e;

		} finally {
			annotationDeploymentTearDown(processEngine, deploymentId);// 卸载流程
			deleteusers();//清除用户信息
			assertAndEnsureCleanDb();// 重置activiti数据库表
			ClockUtil.reset();
		}
	}


和本文相关的两个流程的测试方法:
testAssigneeAssignment();// 执行测试用例
testTaskAssignee();// 执行测试用例

  • 大小: 68.7 KB
  • 大小: 79.6 KB
  • 大小: 40.1 KB
   发表时间:2012-05-02  
补充说明一下,目前activiti-engine-bboss中采用的activiti-engine源码会定期更新和同步activiti svn开发库的源码
0 请登录后投票
   发表时间:2012-05-03  
activiti流程引擎默认的表主键生成机制在多个应用同时使用一个数据库账号时,存在并发主键冲突问题,请大家在activiti.cfg.xml文件中,增加以下配置项替换默认的主键生成机制:
<property name="idGenerator" class="org.activiti.engine.impl.persistence.StrongUuidGenerator" />

配置好的一个activiti.cfg.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>

<properties>

  <property name="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
    <property name="dataSource" factory-class="com.frameworkset.common.poolman.util.SQLManager" factory-method="getTXDatasourceByDBName">
    	<construction>
    		<property value="bspf" />
    	</construction>
    </property>
    <!-- Database configurations -->
    <property name="databaseSchemaUpdate" value="false" />
    
    <!-- job executor configurations -->
    <property name="jobExecutorActivate" value="true" />
    
    <!-- mail server configurations -->
    <property name="mailServerPort" value="5025" />    
    <property name="history" value="audit" />
    <property name="idGenerator" class="org.activiti.engine.impl.persistence.StrongUuidGenerator" />
    
  </property>
    <property name="beanFactory" 
			  factory-class="org.frameworkset.web.servlet.support.WebApplicationContextUtils" 
			  factory-method="getWebApplicationContext"/>
  
</properties>

0 请登录后投票
   发表时间:2012-05-03  
采用org.activiti.engine.impl.persistence.StrongUuidGenerator来生成activiti的主键需要添加一下jar文件:
java-uuid-generator-3.1.2.jar
改包可以在附件中下载
0 请登录后投票
   发表时间:2012-08-06  
提醒一下:
采用bboss 事务框架托管hibernate事务时在执行tm.commit()之前需要调用一下hibernate session对象的flush方法,否则会导致hibernate的更新保存失效的问题:
        TransactionManager tm =  new TransactionManager();  
        try  
        {  
            tm.begin();  
            //业务操作1,采用bboss持久层
            //业务操作2,采用hibernate
            //工作流程操作,基于activiti 5.9工作流引擎,采用mybatis
            session.flush();//务必事务提交之前执行该语句,否则hibernate的更新保存操作无效
            tm.commit();  
            DBUtil.debugStatus();  
        }  
        catch(Throwable e)  
        {  
            try {  
                tm.rollback();  
            } catch (RollbackException e1) {  
                  
                e1.printStackTrace();  
            }  
        }  


各位可能会吐槽为什么会有这么多框架然到一起,呵呵,这是历史遗留问题,为了保证事务一致性,只好借助于bboss的通用事务管理框架了。
0 请登录后投票
   发表时间:2013-03-26  
说明:之所以将初始化activiti流程引擎的逻辑包含在一个bboss 的事务上下文之中,是因为activiti使用的是mybatis 3.0.6版本存在一个缺陷,这个缺陷表现为:先通过sqlsession开启一个事务,然后直接通过sqlsession的getconnection方法获取一个DB链接来执行多次数据库的增删改操作,如果全部执行成功,调用sqlsession的commit方法提交事务,如果执行成功一部分操作后出现异常,再调用sqlsession的rollback回滚事务,这样问题就出来了,mybatis会直接把connection返回给链接池,并不会提交或者回滚之前的db操作(因为这些操作没有调用sqlsession的相关方法来执行,而是直接通过connection完成的),connection并没有被物理关闭而只是返回到池中,这样就导致了之前修改和删除的数据被锁定了,导致后续针对这些锁定的记录的修改操作全部被阻塞造成不可预估的后果。

0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics