流程部署管理
部署一个流程,需要加载两个流程资源文件:
.bpmn流程定义文件.png流程图文件
部署流程,其实就是Activiti框架读取资源文件,并在数据表中生成相应部署实例的记录(包括部署ID等)。流程部署后,才能继续生成流程实例、开启流程。这篇笔记介绍如何部署流程,以及如何读取、删除部署的资源。Activiti中,和部署相关的方法都在RepositoryService接口中。
注:其中,流程图文件是可选的,Activiti能够自动根据流程定义生成流程图,但是它生成的流程图有中文乱码的问题,所以一般还是建议两个文件都部署。
部署流程资源
之前我们写过的例子中,流程资源文件是通过放置在src/main/resources下,然后通过classpath方式加载的;除此之外,在使用activiti-explorer时,我们也可以通过.zip包的形式加载流程。
从classpath加载资源
从classpath加载流程定义的方式,一般适用于不需要用户或管理员自定义部署流程的简单应用。流程定义文件由程序员编写并放置在Java类目录下,系统只支持固定的几个流程。
这里我们有如下目录形式的代码结构:
src/main/resources/
|_activiti
|_UserTaskProcess.bpmn
|_UserTaskProcess.png
从classpath部署流程的例子代码如下:
// 获取repositoryService
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
// 流程定义文件
String bpmnClasspath = "activiti/UserTaskProcess.bpmn";
String pngClasspath = "activiti/UserTaskProcess.png";
// 构建部署
DeploymentBuilder builder = repositoryService.createDeployment();
builder.addClasspathResource(bpmnClasspath);
builder.addClasspathResource(pngClasspath);
builder.deploy();
注意:Activiti引擎部署流程后,流程内容会以longblob字段形式存储在数据库表(act_ge_bytearray)中,而不是需要再次读取classpath下的文件。
从zip包中加载资源
从zip包加载流程定义,一般适用于允许系统管理员用户自行上传流程的较复杂的系统。流程定义的.bpmn和.png两个文件压缩到同一个.zip压缩包中,然后交给Activiti引擎读取。
因为zip包一定是通过客户端(浏览器)上传的,这里我们基于SpringMVC来编写一个例子。具体有关Spring和Activiti整合的内容,将在后续章节详细介绍。
UploadProcessController.java
package com.gacfox.demo.controller;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipInputStream;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.DeploymentBuilder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
@RequestMapping(value = "/upload")
public class UploadProcessController {
@RequestMapping(value = "", method = RequestMethod.GET)
public String index() {
return "upload";
}
@RequestMapping(value = "/do_upload", method = RequestMethod.POST)
public String doUpload(@RequestParam(name = "file") MultipartFile file) {
// 获取repositoryService
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
try {
// 获取上传的zip输入流
InputStream is = file.getInputStream();
ZipInputStream zis = new ZipInputStream(is);
// 构建部署
DeploymentBuilder builder = repositoryService.createDeployment();
builder.addZipInputStream(zis);
builder.deploy();
} catch (IOException e) {
e.printStackTrace();
log.error(e.getMessage());
}
return "success";
}
}
实际上,部署流程的代码和之前classpath方式区别不大,都是使用DeploymentBuilder这个类来读取输入资源,只不过这里读取的是zip输入流ZipInputStream。
读取流程资源
读取部署列表
Activiti的查询接口能够以列表的形式返回所有已部署的流程,下面是一个读取部署列表的例子:
DeployProcessController.java
package com.gacfox.demo.controller;
import java.util.List;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping(value = "/deploy")
public class DeployProcessController {
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String listDeployment(Model model) {
// 获取repositoryService
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
// 查询全部部署的流程
List<ProcessDefinition> definitions = repositoryService.createProcessDefinitionQuery().list();
model.addAttribute("definitions", definitions);
return "definitions";
}
}
下面例子页面,以表格的形式展示读取的相关信息。
definitions.html
<!doctype html>
<html lang="zh-cmn-Hans" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>流程部署管理</title>
</head>
<body>
<div>
<table border="1">
<tr>
<th>流程定义ID</th>
<th>部署ID</th>
<th>流程名</th>
<th>流程定义键</th>
<th>描述信息</th>
<th>流程版本</th>
<th>流程定义BPMN资源名</th>
<th>流程定义PNG资源名</th>
<th>操作</th>
</tr>
<tr th:each="d : ${definitions}">
<td th:text="${d.id}"></td>
<td th:text="${d.deploymentId}"></td>
<td th:text="${d.name}"></td>
<td th:text="${d.key}"></td>
<td th:text="${d.description}"></td>
<td th:text="${d.version}"></td>
<td th:text="${d.resourceName}"></td>
<td th:text="${d.diagramResourceName}"></td>
<td>
<a th:href="@{/deploy/deployment_img(pid=${d.id},resourceName=${d.resourceName})}">XML</a>
<a th:href="@{/deploy/deployment_img(pid=${d.id},resourceName=${d.diagramResourceName})}">流程图</a>
<a th:href="@{/deploy/delete(pid=${d.id})}">删除</a>
</td>
</tr>
</table>
</div>
</body>
</html>
查看流程XML和流程图
流程部署到系统后,查看流程定义和流程图是个很常见的需求,我们只要从Activiti部署的资源中读取两个资源文件就行了。下面是读取部署资源的例子代码:
@RequestMapping(value = "/deployment_img", method = RequestMethod.GET)
public void getProcessImage(String pid, String resourceName, HttpServletResponse response) {
// 获取repositoryService
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
// 读取部署资源
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(pid).singleResult();
// 输出内容
InputStream is = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
byte[] buffer = new byte[1024];
int len = -1;
try {
while ((len = is.read(buffer, 0, 1024)) != -1) {
response.getOutputStream().write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
删除部署
删除部署实现就非常简单了,也是调用RepositoryService相关方法即可。
@RequestMapping(value = "/delete", method = RequestMethod.GET)
public String deleteDeployment(String deploymentId) {
// 获取repositoryService
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
// 删除部署
repositoryService.deleteDeployment(deploymentId);
return "success";
}