Activiti5的前身是jBPM4,而jBPM5是由Drools Flow演化而来的,因此,对于大部分使用开源流程引擎的人来说,从jBPM3或者4升级到Activiti5比较容易,然而,自从Activiti诞生以来,随之而来的有一堆整合的问题:
- Activiti5采用的持久层是MyBatis,而非行业内标准的JPA接口,而且两者耦合程度相当之高。所以对于采用Hibernate或者JPA作为持久层的系统,官方给出的解决方案就是将JPA和MyBatis捏合在一起,采用JPA的EntityManager来管理事务,其整合程度之恶心令人瞠目。
- Acitiviti5采用的容器是Spring Container(包括配置文件),虽然官方也提供了脱离Spring容器的示例,但是无论从User Guide还是Example都对Spring提供了更好的支持,随之而来的问题就是,对于采用JEE环境的项目怎么办?这也是本文需要讨论的重点。
目前看来要想Use Activiti without Spring基本上有以下几种解决方案,如果大家有更好的解决方案欢迎补充:
- 将Spring整合进JEE环境,Spring框架本身是提供JEE的整合解决方案的,但是这种方法是无法without spring的,意味着开发者实际上是管理着Spring的容器。此方法究竟在事务上有无问题,我没有验证过。
- 采用原始的Process Engine,这种方案是最原始的方法,需要解决的问题是引擎在何时启动,又怎样保证引擎的最小化,如果每个EJB都去实例化一个引擎无疑性能将是低下的,因此对于JEE6的环境,可以采用@Singleton和@LocalBean的Bean对Process Engine进行初始化。
/** * Session Bean implementation class ActivitiStarter */ @Singleton @LocalBean public class ActivitiStarter { ProcessEngine processEngine = null; /** * Default constructor. */ public ActivitiStarter() { processEngine = ProcessEngines.getDefaultProcessEngine(); } }
<?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:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd"> <jee:jndi-lookup jndi-name="java:MysqlDS" id="dataSource" /> <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> <property name="dataSource" ref="dataSource" /> <property name="databaseSchemaUpdate" value="true" /> <property name="databaseType" value="mysql" /> <property name="jobExecutorActivate" value="false" /> </bean> </beans>
对于JEE5的环境,也许只能硬编码,写一个多线程的Singleton来对流程引擎进行初始化。
这种方法比较原始,可以说基本上流程引擎是用编程式实现的,脱离了容器了,因此需要自己去维护引擎的初始化。public class ActivitiStart { private static ThreadLocal initHolder = new ThreadLocal(); private static ProcessEngine engine; private static String DB_JNDI = "java:MysqlDS"; public static ProcessEngine getEngineInstance () { if (initHolder.get() == null) { synchronized (initHolder) { if (engine == null) { ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration(); cfg.setDataSourceJndiName(DB_JNDI); cfg.setTransactionsExternallyManaged(true); cfg.setKpbExecutorActivate(true); engine = cfg.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); } initHolder.set(Boolean.TRUE); } } return engine; } }
- 最佳的解决方案其实是重写流程的初始化,讲流程引擎完全从Spring容器中脱离出来,放在JEE的容器中。好在已经有人做了这方面的工作。
基本的实现思路是利用jBoss Bean代替Spring Bean<deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="ProcessEngineManager" class="com.camunda.fox.activiti.enterprise.service.impl.ProcessEngineManager"> <property name="processEngineConfiguration"><inject bean="ProcessEngineConfiguration" /></property> <property name="dataSourceJndiName">java:MysqlDS</property> </bean> <bean name="ProcessEngineConfiguration" class="org.activiti.engine.impl.cfg.JtaProcessEngineConfiguration"> <property name="transactionManager"><inject bean="RealTransactionManager" /></property> <install method="setTransactionsExternallyManaged"><parameter><value>true</value></parameter></install> <install method="setDatabaseSchemaUpdate"><parameter><value>true</value></parameter></install> <install method="setMailServerHost"><parameter><value>localhost</value></parameter></install> <install method="setMailServerPort"><parameter><value>2525</value></parameter></install> <install method="setJobExecutorActivate"><parameter><value>true</value></parameter></install> </bean> </deployment>
在使用时只需要
值得注意的是,这里的RuntimeService并不是引擎的原生实现的,而是第三方加载实现的。@Stateless public class StartProcessEjb implements StartProcessLocal, StartProcessRemote{ @EJB RuntimeService runtimeService; public void startProcess() { runtimeService.startProcessInstanceByKey("someProcess"); } }
目前看三种方式各自有利弊,没有哪种算是最完美的解决方案,比较完美的应该算是第三种,但是在事务的处理上,还需要进一步探讨。
第二、第三种方案的源码在附件中都有。