jBPM-4.4与Spring集成配置比较容易,这里我使用的是Spring-2.5.6,数据库连接池使用C3P0,将相关的两个jar文件加入到CLASSPATH中。
jBPM-4.4与Spring集成的配置过程说明如下。
配置
1、修改jbpm.cfg.xml配置文件
将jbpm.cfg.xml文件中<import resource="jbpm.tx.hibernate.cfg.xml" />一行,修改为 <import resource="jbpm.tx.spring.cfg.xml" />,我们使用Spring的事务管理器,修改后配置文件内容为:
[xhtml] view plaincopy
- <?xml version="1.0" encoding="UTF-8"?>
- <jbpm-configuration>
- <import resource="jbpm.default.cfg.xml" />
- <import resource="jbpm.businesscalendar.cfg.xml" />
- <!-- <import resource="jbpm.tx.hibernate.cfg.xml" /> -->
- <import resource="jbpm.tx.spring.cfg.xml" />
- <import resource="jbpm.jpdl.cfg.xml" />
- <import resource="jbpm.bpmn.cfg.xml" />
- <import resource="jbpm.identity.cfg.xml" />
- <!-- Job executor is excluded for running the example test cases. -->
- <!-- To enable timers and messages in production use, this should be included. -->
- <!--
- <import resource="jbpm.jobexecutor.cfg.xml" />
- -->
- </jbpm-configuration>
2、修改Spring的applicationContext.xml配置文件
配置文件内容如下所示:
[xhtml] view plaincopy
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
- destroy-method="close">
- <property name="driverClass" value="com.mysql.jdbc.Driver" />
- <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/jbpmdb" />
- <property name="user" value="jbpm" />
- <property name="password" value="jbpm" />
- <property name="initialPoolSize" value="3" />
- <property name="minPoolSize" value="3" />
- <property name="maxPoolSize" value="50" />
- <property name="maxIdleTime" value="600" />
- <property name="maxStatements" value="100" />
- <property name="acquireIncrement" value="3" />
- </bean>
- <bean id="springHelper" class="org.jbpm.pvm.internal.processengine.SpringHelper" />
- <bean id="processEngine" factory-bean="springHelper"
- factory-method="createProcessEngine" />
- <!--Hibernate SessionFatory -->
- <bean id="sessionFactory"
- class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
- <property name="dataSource" ref="dataSource" />
- <property name="mappingResources">
- <list>
- <value>jbpm.repository.hbm.xml</value>
- <value>jbpm.execution.hbm.xml</value>
- <value>jbpm.history.hbm.xml</value>
- <value>jbpm.task.hbm.xml</value>
- <value>jbpm.identity.hbm.xml</value>
- </list>
- </property>
- <property name="hibernateProperties">
- <props>
- <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
- <prop key="hibernate.hbm2ddl.auto">create-drop</prop>
- <prop key="hibernate.show_sql">true</prop>
- </props>
- </property>
- </bean>
- <!-- Transaction Manager -->
- <bean id="transactionManager"
- class="org.springframework.orm.hibernate3.HibernateTransactionManager">
- <property name="sessionFactory" ref="sessionFactory" />
- <property name="dataSource" ref="dataSource" />
- </bean>
- </beans>
这里使用MySQL作为流程数据库,需要在dataSource的bean中配置,其他的配置内容非常容易理解。这里面,我们最终的目标是通过 Spring的IOC容器来管理jBPM引擎的创建,即在应用中获取到一个org.jbpm.api.ProcessEngine实例即可,在一个应用中 通常只有一个流程引擎实例,然后通过这个实例就可以获取到流程提供的6种基本服务:
org.jbpm.api.RepositoryService
org.jbpm.api.ExecutionService
org.jbpm.api.ManagementService
org.jbpm.api.TaskService
org.jbpm.api.HistoryService
org.jbpm.api.IdentityService
一般情况下,在开发中不要将org.jbpm.api.ProcessEngine实例暴露给实际开发者,而只是暴露出引擎提供的服务即可。
测试验证
我们测试用例的流程定义如图所示:
对应的流程定义文件内容如下所示:
[xhtml] view plaincopy
- <?xml version="1.0" encoding="UTF-8"?>
- <process name="MyExample" xmlns="http://jbpm.org/4.4/jpdl">
- <on event="start">
- <event-listener class="com.umpay.workflow.jbpm.listener.StartupListener">
- <field name="status">
- <string value="startup check..."/>
- </field>
- </event-listener>
- </on>
- <start g="354,6,48,48">
- <transition g="-102,-16" name="to start decision" to="start decision">
- <event-listener class="com.umpay.workflow.jbpm.listener.InitializationListener">
- <field name="initialized">
- <string value="initialize..."/>
- </field>
- </event-listener>
- </transition>
- </start>
- <decision g="413,107,48,48" name="start decision">
- <handler class="com.umpay.workflow.jbpm.decision.StartDecision" />
- <transition g="-83,-17" name="to check user" to="check user"/>
- <transition g="-48,-25" name="to error" to="error"/>
- </decision>
- <state g="216,183,104,52" name="check user">
- <on event="start">
- <event-listener class="com.umpay.workflow.jbpm.listener.CheckUserListener">
- <field name="state">
- <string value="Check user state..."/>
- </field>
- </event-listener>
- </on>
- <transition g="-128,-8" name="to decide check user" to="decide check user result"/>
- </state>
- <decision g="359,271,48,48" name="decide check user result">
- <handler class="com.umpay.workflow.jbpm.decision.CheckUserDecision" />
- <transition g="-95,-17" name="to check mobile" to="check mobile"/>
- <transition g="-41,14" name="to error" to="error"/>
- </decision>
- <state g="215,349,104,52" name="check mobile">
- <on event="start">
- <event-listener class="com.umpay.workflow.jbpm.listener.CheckMobileListener">
- <field name="online">
- <string value="Check mobile number..."/>
- </field>
- </event-listener>
- </on>
- <transition g="-146,-6" name="to decide check mobile" to="decide check mobile result"/>
- </state>
- <decision g="337,435,48,48" name="decide check mobile result">
- <handler class="com.umpay.workflow.jbpm.decision.CheckMobileDecision" />
- <transition g="-98,-21" name="to do transaction" to="do transaction"/>
- <transition g="-39,9" name="to error" to="error"/>
- </decision>
- <state g="217,516,112,52" name="do transaction">
- <on event="start">
- <event-listener class="com.umpay.workflow.jbpm.listener.DoTransactionListener">
- <field name="operation">
- <string value="Do transaction..."/>
- </field>
- </event-listener>
- </on>
- <transition g="-101,-17" name="to last decision" to="last decision"/>
- </state>
- <decision g="312,599,48,48" name="last decision">
- <handler class="com.umpay.workflow.jbpm.decision.DoTransactionDecision" />
- <transition g="-41,-17" name="to end" to="end"/>
- <transition g="-41,-1" name="to error" to="error"/>
- </decision>
- <end-error g="475,675,48,48" name="error"/>
- <end g="242,675,48,48" name="end">
- <on event="start">
- <event-listener class="com.umpay.workflow.jbpm.listener.ShutdownHookListener">
- <field name="saveThings">
- <string value="S:shutdown..."/>
- </field>
- </event-listener>
- </on>
- </end>
- </process>
基于该流程定义,实现的测试用例如下所示:
[java] view plaincopy
- package com.umpay.workflow.jbpm.process;
- import junit.framework.TestCase;
- import org.jbpm.api.Execution;
- import org.jbpm.api.ProcessEngine;
- import org.jbpm.api.ProcessInstance;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- public class MyProcessWithSpringTest extends TestCase {
- private ProcessEngine processEngine;
- String deploymentId;
- protected void setUp() throws Exception {
- super.setUp();
- ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
- "applicationContext.xml");
- ctx.start();
- this.processEngine = (ProcessEngine) ctx.getBean("processEngine");
- deploymentId = this.processEngine
- .getRepositoryService()
- .createDeployment()
- .addResourceFromClasspath(
- "com/umpay/workflow/jbpm/process/process.jpdl.xml")
- .deploy();
- }
- protected void tearDown() throws Exception {
- this.processEngine.getRepositoryService().deleteDeploymentCascade(
- deploymentId);
- super.tearDown();
- }
- public void testMyProcess() {
- // start a process instance
- ProcessInstance processInstance = this.processEngine
- .getExecutionService().startProcessInstanceByKey("MyExample");
- // state : check user
- assertEquals("check user", processInstance.findActiveActivityNames()
- .iterator().next());
- Execution execution = processInstance
- .findActiveExecutionIn("check user");
- // state : check mobile
- processInstance = this.processEngine.getExecutionService()
- .signalExecutionById(execution.getId());
- execution = processInstance.findActiveExecutionIn("check mobile");
- assertEquals("check mobile", processInstance.findActiveActivityNames()
- .iterator().next());
- // state : do transaction
- processInstance = this.processEngine.getExecutionService()
- .signalExecutionById(execution.getId());
- execution = processInstance.findActiveExecutionIn("do transaction");
- assertEquals("do transaction", processInstance
- .findActiveActivityNames().iterator().next());
- processInstance = this.processEngine.getExecutionService()
- .signalExecutionById(execution.getId());
- assertEquals(true, processInstance.findActiveActivityNames().isEmpty());
- // ended
- assertEquals(Execution.STATE_ENDED, processInstance.getState());
- }
- }