Liferay7 BPM门户开发之3: Activiti开发环境搭建
下载地址:
http://activiti.org/download.html
源码:
https://github.com/Activiti/Activiti
环境准备(检查项):
-
- JDK 1.7 + JAVA_HOME :java -version
- 下载 Eclipse Indigo +
- 拷贝activiti-explorer.war 到 Tomcat/webapps目录
- 运行Tomcat/bin目录startup.bat or startup.sh
- 不修改Tomcat http端口情况下访问:http://localhost:8080/activiti-explorer
-
如果不想使用in-memory H2 database(默认演示数据库),修改WEB-INF/classes下文件db.properties;
默认情况下,会创建user and groups, process definitions and models演示数据,如果不想建立,则设置WEB-INF/classes/engine.properties文件,修改为false - jar包依赖下载,通过Maven:
mvn dependency:copy-dependencies
on a module of the Activiti source code. - jar包依赖下载,通过放入classpath方式(直接引入依赖),下载地址:http://download.csdn.net/detail/starcrm/9650018
-
org.activiti:activiti-engine:jar:5.17.0
+- org.activiti:activiti-bpmn-converter:jar:5.17.0:compile
| \- org.activiti:activiti-bpmn-model:jar:5.17.0:compile
| +- com.fasterxml.jackson.core:jackson-core:jar:2.2.3:compile
| \- com.fasterxml.jackson.core:jackson-databind:jar:2.2.3:compile
| \- com.fasterxml.jackson.core:jackson-annotations:jar:2.2.3:compile
+- org.activiti:activiti-process-validation:jar:5.17.0:compile
+- org.activiti:activiti-image-generator:jar:5.17.0:compile
+- 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.3.2:compile
+- org.mybatis:mybatis:jar:3.2.5:compile
+- org.springframework:spring-beans:jar:4.0.6.RELEASE:compile
| \- org.springframework:spring-core:jar:4.0.6.RELEASE:compile
+- joda-time:joda-time:jar:2.6:compile
+- org.slf4j:slf4j-api:jar:1.7.6:compile
+- org.slf4j:jcl-over-slf4j:jar:1.7.6:compile
-
-
-
演示用户ID
-
UserId | Password | Security roles |
---|---|---|
kermit |
kermit |
admin |
gonzo |
gonzo |
manager |
fozzie |
fozzie |
user |
开发准备:
配置项:activiti.cfg.xml
Spring配置:
<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="asyncExecutorEnabled" value="true" /> <property name="asyncExecutorActivate" value="false" /> <property name="mailServerHost" value="mail.my-corp.com" /> <property name="mailServerPort" value="5025" /> </bean> </beans>
- 一般情况可以这样创建引擎实例(即读取activiti.cfg.xml):ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine()
- 也可以使用beanid:ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource, String beanName);
- 也可以通过程序设置:
ProcessEngine processEngine = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration() .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE) .setJdbcUrl("jdbc:h2:mem:my-own-db;DB_CLOSE_DELAY=1000") .setAsyncExecutorEnabled(true) .setAsyncExecutorActivate(false) .buildProcessEngine();
DBCP 数据连接方式
<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" /> ...
JNDI Datasource数据连接方式
配置db.properties配置JNDI datasource:
activiti-webapp-explorer2/src/webapp/WEB-INF/activiti-standalone-context.xml
activiti-webapp-rest2/src/main/resources/activiti-context.xml
删除 beans named "dbProperties" and "dataSource".
添加:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:comp/env/jdbc/activitiDB"/> </bean>
替换activiti-webapp-explorer2/src/main/webapp/META-INF/context.xml:
<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-webapp-rest2/src/main/webapp/META-INF/context.xml :
<?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>
Tomcat 环境下, the JNDI resource:
$CATALINA_BASE/conf/[enginename]/[hostname]/[warname].xml
$CATALINA_BASE/conf/Catalina/localhost/activiti-explorer.xml
<?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>
支持的数据库
databases :
Activiti database type | Example JDBC URL | Notes |
---|---|---|
h2 |
jdbc:h2:tcp://localhost/activiti |
Default configured database |
mysql |
jdbc:mysql://localhost:3306/activiti?autoReconnect=true |
Tested using mysql-connector-java database driver |
oracle |
jdbc:oracle:thin:@localhost:1521:xe |
|
postgres |
jdbc:postgresql://localhost:5432/activiti |
|
db2 |
jdbc:db2://localhost:50000/activiti |
|
mssql |
jdbc:sqlserver://localhost:1433;databaseName=activiti (jdbc.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver)OR jdbc:jtds:sqlserver://localhost:1433/activiti (jdbc.driver=net.sourceforge.jtds.jdbc.Driver) |
Tested using Microsoft JDBC Driver 4.0 (sqljdbc4.jar) and JTDS Driver |
建立数据库表和索引的方式1:通过程序建立
-
Add the activiti-engine jars to your classpath
-
Add a suitable database driver,比如mysql-connector-java
-
添加配置文件 (activiti.cfg.xml) to your classpath
-
Execute the main method of the DbSchemaCreate class
建立数据库表和索引的方式2:运行SQL脚本
SQL脚本在 database
/create 目录
前缀缩写含义
-
ACT_RE_*: RE stands for
repository
. -
ACT_RU_*: RU stands for
runtime
. runtime data of process instances, user tasks, variables, jobs, etc. -
ACT_ID_*: ID stands for
identity
. users, groups... -
ACT_HI_*: HI stands for
history
. past process instances, variables, tasks... -
ACT_GE_*:
general
data, which is used in various use cases.
主要的类
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); RuntimeService runtimeService = processEngine.getRuntimeService(); RepositoryService repositoryService = processEngine.getRepositoryService(); TaskService taskService = processEngine.getTaskService(); ManagementService managementService = processEngine.getManagementService(); IdentityService identityService = processEngine.getIdentityService(); HistoryService historyService = processEngine.getHistoryService(); FormService formService = processEngine.getFormService();
通过程序部署流程
新建 xml file VacationRequest.bpmn20.xml
in the src/test/resources/org/activiti/test
resource folder
<?xml version="1.0" encoding="UTF-8" ?> <definitions id="definitions" targetNamespace="http://activiti.org/bpmn20" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn"> <process id="vacationRequest" name="Vacation request"> <startEvent id="request" activiti:initiator="employeeName"> <extensionElements> <activiti:formProperty id="numberOfDays" name="Number of days" type="long" value="1" required="true"/> <activiti:formProperty id="startDate" name="First day of holiday (dd-MM-yyy)" datePattern="dd-MM-yyyy hh:mm" type="date" required="true" /> <activiti:formProperty id="vacationMotivation" name="Motivation" type="string" /> </extensionElements> </startEvent> <sequenceFlow id="flow1" sourceRef="request" targetRef="handleRequest" /> <userTask id="handleRequest" name="Handle vacation request" > <documentation> ${employeeName} would like to take ${numberOfDays} day(s) of vacation (Motivation: ${vacationMotivation}). </documentation> <extensionElements> <activiti:formProperty id="vacationApproved" name="Do you approve this vacation" type="enum" required="true"> <activiti:value id="true" name="Approve" /> <activiti:value id="false" name="Reject" /> </activiti:formProperty> <activiti:formProperty id="managerMotivation" name="Motivation" type="string" /> </extensionElements> <potentialOwner> <resourceAssignmentExpression> <formalExpression>management</formalExpression> </resourceAssignmentExpression> </potentialOwner> </userTask> <sequenceFlow id="flow2" sourceRef="handleRequest" targetRef="requestApprovedDecision" /> <exclusiveGateway id="requestApprovedDecision" name="Request approved?" /> <sequenceFlow id="flow3" sourceRef="requestApprovedDecision" targetRef="sendApprovalMail"> <conditionExpression xsi:type="tFormalExpression">${vacationApproved == 'true'}</conditionExpression> </sequenceFlow> <task id="sendApprovalMail" name="Send confirmation e-mail" /> <sequenceFlow id="flow4" sourceRef="sendApprovalMail" targetRef="theEnd1" /> <endEvent id="theEnd1" /> <sequenceFlow id="flow5" sourceRef="requestApprovedDecision" targetRef="adjustVacationRequestTask"> <conditionExpression xsi:type="tFormalExpression">${vacationApproved == 'false'}</conditionExpression> </sequenceFlow> <userTask id="adjustVacationRequestTask" name="Adjust vacation request"> <documentation> Your manager has disapproved your vacation request for ${numberOfDays} days. Reason: ${managerMotivation} </documentation> <extensionElements> <activiti:formProperty id="numberOfDays" name="Number of days" value="${numberOfDays}" type="long" required="true"/> <activiti:formProperty id="startDate" name="First day of holiday (dd-MM-yyy)" value="${startDate}" datePattern="dd-MM-yyyy hh:mm" type="date" required="true" /> <activiti:formProperty id="vacationMotivation" name="Motivation" value="${vacationMotivation}" type="string" /> <activiti:formProperty id="resendRequest" name="Resend vacation request to manager?" type="enum" required="true"> <activiti:value id="true" name="Yes" /> <activiti:value id="false" name="No" /> </activiti:formProperty> </extensionElements> <humanPerformer> <resourceAssignmentExpression> <formalExpression>${employeeName}</formalExpression> </resourceAssignmentExpression> </humanPerformer> </userTask> <sequenceFlow id="flow6" sourceRef="adjustVacationRequestTask" targetRef="resendRequestDecision" /> <exclusiveGateway id="resendRequestDecision" name="Resend request?" /> <sequenceFlow id="flow7" sourceRef="resendRequestDecision" targetRef="handleRequest"> <conditionExpression xsi:type="tFormalExpression">${resendRequest == 'true'}</conditionExpression> </sequenceFlow> <sequenceFlow id="flow8" sourceRef="resendRequestDecision" targetRef="theEnd2"> <conditionExpression xsi:type="tFormalExpression">${resendRequest == 'false'}</conditionExpression> </sequenceFlow> <endEvent id="theEnd2" /> </process> </definitions>
部署:
package activitiLearn; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.RepositoryService; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; public class test { public static void main(String[] args) { // TODO Auto-generated method stub PropertyConfigurator.configure("bin/log4j.properties"); Logger log=Logger.getLogger(test.class); ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); RepositoryService repositoryService = processEngine.getRepositoryService(); repositoryService.createDeployment() .addClasspathResource("bpmn20/VacationRequest.bpmn20.xml") .deploy(); log.info("Number of process definitions: " + repositoryService.createProcessDefinitionQuery().count()); } }
输出:
2016-10-10 21:04:55 INFO [org.activiti.engine.ProcessEngines] Initializing process engine using configuration 'file:/D:/workspace/activitiLearn/bin/activiti.cfg.xml'
2016-10-10 21:04:55 INFO [org.activiti.engine.ProcessEngines] initializing process engine for resource file:/D:/workspace/activitiLearn/bin/activiti.cfg.xml
2016-10-10 21:04:56 INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] Loading XML bean definitions from resource loaded through InputStream
2016-10-10 21:04:58 INFO [org.activiti.engine.impl.db.DbSqlSession] performing create on engine with resource org/activiti/db/create/activiti.h2.create.engine.sql
2016-10-10 21:04:58 INFO [org.activiti.engine.impl.db.DbSqlSession] performing create on history with resource org/activiti/db/create/activiti.h2.create.history.sql
2016-10-10 21:04:58 INFO [org.activiti.engine.impl.db.DbSqlSession] performing create on identity with resource org/activiti/db/create/activiti.h2.create.identity.sql
2016-10-10 21:04:58 INFO [org.activiti.engine.impl.ProcessEngineImpl] ProcessEngine default created
2016-10-10 21:04:58 INFO [org.activiti.engine.ProcessEngines] initialised process engine default
2016-10-10 21:04:58 INFO [org.activiti.engine.impl.bpmn.deployer.BpmnDeployer] Processing resource bpmn20/VacationRequest.bpmn20.xml
2016-10-10 21:04:58 INFO [org.test] Number of process definitions: 1
打开管理后台看看,已经成功部署了
开始一个流程实例:
Map<String, Object> variables = new HashMap<String, Object>(); variables.put("employeeName", "Kermit"); variables.put("numberOfDays", new Integer(4)); variables.put("vacationMotivation", "I'm really tired!"); RuntimeService runtimeService = processEngine.getRuntimeService(); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("vacationRequest", variables); // Verify that we started a new process instance Log.info("Number of process instances: " + runtimeService.createProcessInstanceQuery().count());
查询任务:
TaskService taskService = processEngine.getTaskService(); List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("management").list(); for (Task task : tasks) { Log.info("Task available: " + task.getName()); }
完成任务:
Task task = tasks.get(0); Map<String, Object> taskVariables = new HashMap<String, Object>(); taskVariables.put("vacationApproved", "false"); taskVariables.put("managerMotivation", "We have a tight deadline!"); taskService.complete(task.getId(), taskVariables);
流程挂起:
repositoryService.suspendProcessDefinitionByKey("vacationRequest"); try { runtimeService.startProcessInstanceByKey("vacationRequest"); } catch (ActivitiException e) { e.printStackTrace(); }
挂起后重启:
runtimeService.activateProcessInstanceXXX
任务查询:
只能实现AND逻辑
List<Task> tasks = taskService.createTaskQuery() .taskAssignee("kermit") .processVariableValueEquals("orderId", "0815") .orderByDueDate().asc() .list();
复杂逻辑比如OR逻辑需要自己写SQL
List<Task> tasks = taskService.createNativeTaskQuery() .sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T WHERE T.NAME_ = #{taskName}") .parameter("taskName", "gonzoTask") .list(); long count = taskService.createNativeTaskQuery() .sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T1, " + managementService.getTableName(VariableInstanceEntity.class) + " V1 WHERE V1.TASK_ID_ = T1.ID_") .count();
流程变量
一个流程实例可以有任意多的变量process variables,变量用来传递数据。
变量存储在数据表ACT_RU_VARIABLE
通过RuntimeService建立:ProcessInstance startProcessInstanceByKey(String processDefinitionKey, Map<String, Object> variables);
在实例运行时,也可以随时设置变量:
void setVariable(String executionId, String variableName, Object value); void setVariableLocal(String executionId, String variableName, Object value); void setVariables(String executionId, Map<String, ? extends Object> variables); void setVariablesLocal(String executionId, Map<String, ? extends Object> variables);
Map<String, Object> getVariables(String executionId); Map<String, Object> getVariablesLocal(String executionId); Map<String, Object> getVariables(String executionId, Collection<String> variableNames); Map<String, Object> getVariablesLocal(String executionId, Collection<String> variableNames); Object getVariable(String executionId, String variableName); <T> T getVariable(String executionId, String variableName, Class<T> variableClass); execution.getVariables(); execution.getVariables(Collection<String> variableNames); execution.getVariable(String variableName); execution.setVariables(Map<String, object> variables); execution.setVariable(String variableName, Object value);
TaskService也可以使用getVariables,这意味着can have local variables that are alive just for the duration of the task
目前维护的开源产品:https://gitee.com/475660