1. 整合初衷
Activiti上手也有两三个月了,今天写一个springmvc框架整合Activiti 流程设计器的文章,我用的activiti版本是5.16。
通过Modeler设计的流程模型可以直接部署到引擎,也可以把已经部署的流程转换为Model从而在Modeler中编辑。
2. 整合工作
2.1下载源码
首先需要从Github下载源码: https://github.com/Activiti/Activiti;可以直接用Git克隆,也可以下载zip格式的压缩包。
2.2复制文件
复制的所有文件均在 activiti-webapp-explorer2目录中。
- src/main/resources中的editor.html、stencilset.json、plugins.xml到项目源码的源码根目录,保证编译之后在classes根目录
- src/main/webapp中的api、editor、explorer、libs到项目的webapp目录(与WEB-INF目录同级)
图片如下
2.3添加pom.xml依赖
没有测试过,我用的jar包
<dependency>
<groupid>org.activiti</groupid>
<artifactid>activiti-explorer</artifactid>
<version>5.16</version>
<exclusions>
<exclusion>
<artifactid>vaadin</artifactid>
<groupid>com.vaadin</groupid>
</exclusion>
<exclusion>
<artifactid>dcharts-widget</artifactid>
<groupid>org.vaadin.addons</groupid>
</exclusion>
<exclusion>
<artifactid>activiti-simple-workflow</artifactid>
<groupid>org.activiti</groupid>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupid>org.activiti</groupid>
<artifactid>activiti-modeler</artifactid>
<version>5.16</version>
</dependency>
2.4添加ExplorerRestApplication.java类
package com.isoftstone.cms.workflow.org.activiti.explorer.rest;
import org.activiti.rest.common.api.DefaultResource;
import org.activiti.rest.common.application.ActivitiRestApplication;
import org.activiti.rest.common.filter.JsonpFilter;
import org.activiti.rest.diagram.application.DiagramServicesInit;
import org.activiti.rest.editor.application.ModelerServicesInit;
import org.restlet.Restlet;
import org.restlet.routing.Router;
public class ExplorerRestApplication extends ActivitiRestApplication {
public ExplorerRestApplication() {
super();
}
/**
* Creates a root Restlet that will receive all incoming calls.
*/
@Override
public synchronized Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attachDefault(DefaultResource.class);
ModelerServicesInit.attachResources(router);
DiagramServicesInit.attachResources(router);
JsonpFilter jsonpFilter = new JsonpFilter(getContext());
jsonpFilter.setNext(router);
return jsonpFilter;
}
}
2.5配置web.xml
<!-- 流程配置开始 -->
<!-- Restlet adapter, used to expose modeler functionality through REST -->
<servlet>
<servlet-name>ExplorerRestletServlet</servlet-name>
<servlet-class>org.restlet.ext.servlet.ServerServlet</servlet-class>
<init-param>
<!-- Application class name -->
<param-name>org.restlet.application</param-name>
<param-value>com.isoftstone.cms.workflow.org.activiti.explorer.rest.ExplorerRestApplication</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>RestletServlet</servlet-name>
<servlet-class>org.restlet.ext.servlet.ServerServlet</servlet-class>
<init-param>
<!-- Application class name -->
<param-name>org.restlet.application</param-name>
<param-value>org.activiti.rest.service.application.ActivitiRestServicesApplication</param-value>
</init-param>
</servlet>
<!-- Catch all service requests -->
<servlet-mapping>
<servlet-name>ExplorerRestletServlet</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RestletServlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<!-- 流程配置结束 -->
2.6配置spring文件applicationContext.xml
<!--工作流配置 开始-->
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="publicDataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="false" />
<property name="history" value="full"/>
<property name="enableDatabaseEventLogging" value="false" />
<property name="databaseType" value="mysql"/>
<!-- 自定义表单字段类型 -->
<!-- <property name="customFormTypes">
<list>
<bean class="com.iss.itreasury.workflow.form.UsersFormType"/>
</list>
</property> -->
<!-- 生成流程图的字体 宋体=\u5B8B\u4F53-->
<property name="activityFontName" value="\u5B8B\u4F53"/>
<property name="labelFontName" value="\u5B8B\u4F53"/>
</bean>
<!-- 事务管理器配置, 单数据源事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="publicDataSource" />
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
<bean id="formService" factory-bean="processEngine" factory-method="getFormService" />
<bean id="identityService" factory-bean="processEngine" factory-method="getIdentityService" />
<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
<bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" />
<bean class="com.isoftstone.cms.workflow.util.ActivtiManager" init-method="initMethod" lazy-init="false">
<property name="repositoryService" ref="repositoryService" />
<property name="runtimeService" ref="runtimeService" />
<property name="formService" ref="formService" />
<property name="identityService" ref="identityService" />
<property name="taskService" ref="taskService" />
<property name="historyService" ref="historyService" />
<property name="managementService" ref="managementService" />
</bean>
<!--工作流配置 结束-->
2.7配置spring文件DispatcherServlet-servlet.xml
配置静态资源访问路径
<mvc:resources location="/editor/" mapping="/editor/**"/>
<mvc:resources location="/explorer/" mapping="/explorer/**"/>
<mvc:resources location="/libs/" mapping="/libs/**"/>
2.8编写控制器
package com.isoftstone.cms.workflow.api;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.isoftstone.cms.common.pojo.AjaxDataEntity;
import com.isoftstone.cms.component.datagrid.pojo.DataGrid;
import com.isoftstone.cms.component.datagrid.pojo.DataResult;
import com.isoftstone.cms.component.datagrid.pojo.DataStyle;
import com.isoftstone.cms.component.datagrid.pojo.PangingParam;
import com.isoftstone.cms.component.datagrid.utils.DataTypeConstant;
import com.isoftstone.cms.component.datagrid.utils.PangingTools;
import com.isoftstone.cms.syscore.api.BaseController;
import com.isoftstone.cms.syscore.api.JSONFactory;
import com.isoftstone.cms.syscore.utils.MessageResourcesUtils;
import com.isoftstone.cms.syscore.utils.ServletUtils;
import com.isoftstone.cms.workflow.util.ActivtiManager;
@Scope("prototype")
@Controller
public class ModelController extends BaseController{
private static Logger logger = LoggerFactory.getLogger(ModelController.class);
private ActivtiManager activtiManager = ActivtiManager.getDefaultManage();
/**
* 点击菜单"模版管理"显示“流程模板列表”列表
* @param request
* @param response
* @param model
* @return
*/
@RequestMapping(value = "/workflow/model/modelList.web")
public String queryProvinceList(HttpServletRequest request, HttpServletResponse response) {
return "/WEB-INF/jsp/workflow/model/modelList";
}
/**
* 打开“模版管理”列表加载列表数据
* @param request
* @param response
* @param model
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@RequestMapping(value = "/workflow/model/queryModelListInfoDataGrid.json")
public void queryModelListInfoDataGrid(HttpServletRequest request, HttpServletResponse response) {
PangingParam pangingParam = null;
DataResult dataResult = new DataResult();
DataGrid dataGrid = null;
try {
pangingParam = (PangingParam) ServletUtils.getParametersToBaseEntity(request, PangingParam.class);
HashMap<String, Object> map = (HashMap) ServletUtils.getParametersToHashMap(request);
dataGrid = new DataGrid();
dataGrid.setPangingParam(pangingParam);
/* map.put("startrow", dataGrid.getStartrow());
map.put("endrow", dataGrid.getEndrow());
map.put("rows", dataGrid.getRows());
map.put("sort", dataGrid.getSort());
map.put("order", dataGrid.getOrder());
map.put("statusid", SysConstant.RecordStatus.VALID);*/
RepositoryService repositoryService = activtiManager.getRepositoryService();
long resultCount = repositoryService.createModelQuery().count();
List<org.activiti.engine.repository.Model> resultList = repositoryService.createModelQuery().orderByCreateTime().desc().listPage((int)(pangingParam.getPage()-1), (int)dataGrid.getRows());
DataStyle dataStyle = new DataStyle();
dataStyle.setPageIndex(pangingParam.getPage());
dataStyle.setDataCount(resultCount);
dataStyle.setDataList(resultList);
dataStyle.addDepict("id", DataTypeConstant.STRING);
dataStyle.addDepict("name", DataTypeConstant.STRING);
dataStyle.addDepict("key", DataTypeConstant.STRING);
dataStyle.addDepict("version", DataTypeConstant.LONG);
dataStyle.addDepict("createTime", DataTypeConstant.DATETIME);
dataStyle.addDepict("lastUpdateTime", DataTypeConstant.DATETIME);
dataStyle.addDepict("metaInfo", DataTypeConstant.STRING);
dataResult = PangingTools.buildResultPagerInfo(dataStyle);
if (pangingParam.getReportExportType() == 0) {
response.getWriter().write(JSONFactory.toJSONString(dataResult, userContext));
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 跳转流程模型新增页面
* @param request
* @param response
* @param model
* @return
*/
@RequestMapping(value = "/workflow/model/toAddModel.web")
public String toAddModel(HttpServletRequest request, HttpServletResponse response) {
return "/WEB-INF/jsp/workflow/model/modelAdd";
}
/**
* 新增流程模型
*/
@RequestMapping(value = "/workflow/model/addModel.web", method = { RequestMethod.GET, RequestMethod.POST })
public String addModel(HttpServletRequest request, HttpServletResponse response) {
String modelId ="";
try {
String name = request.getParameter("name");
String key = request.getParameter("key");
String description = StringUtils.defaultString(request.getParameter("description"));
RepositoryService repositoryService = activtiManager.getRepositoryService();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode editorNode = objectMapper.createObjectNode();
editorNode.put("id", "canvas");
editorNode.put("resourceId", "canvas");
ObjectNode stencilSetNode = objectMapper.createObjectNode();
stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
editorNode.put("stencilset", stencilSetNode);
Model modelData = repositoryService.newModel();
ObjectNode modelObjectNode = objectMapper.createObjectNode();
modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, name);
modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
modelData.setMetaInfo(modelObjectNode.toString());
modelData.setName(name);
modelData.setKey(StringUtils.defaultString(key));
//存入ACT_RE_MODEL
repositoryService.saveModel(modelData);
//存入ACT_GE_BYTEARRAY
repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));
modelId = modelData.getId();
} catch (Exception e) {
logger.error("创建模型失败:", e);
}
return "redirect:/service/editor?id="+ modelId;
}
/**
* 删除流程模型
* @param request
* @param response
* @param model
*/
@RequestMapping(value = "/workflow/model/deleteModel.json", method = { RequestMethod.GET, RequestMethod.POST })
public void deleteModel(HttpServletRequest request, HttpServletResponse response, Model model) {
AjaxDataEntity ajaxDataEntity = new AjaxDataEntity();
try {
String modelId = getRequest().getParameter("id");
RepositoryService repositoryService = activtiManager.getRepositoryService();
repositoryService.deleteModel(modelId);
getUserContext().setInfoMessage(MessageResourcesUtils.getMessage(request, "alertinfo.common.java.alert3"));
} catch (Exception e) {
e.printStackTrace();
getUserContext().setErrorMessage(MessageResourcesUtils.getMessage(request, "alertinfo.common.java.alert4"));
}finally {
try {
response.getWriter().write(JSONFactory.toJSONString(ajaxDataEntity, getUserContext()));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 根据Model部署
*/
@RequestMapping(value = "/workflow/model/deploy.json",method = { RequestMethod.GET, RequestMethod.POST })
public void deploy(HttpServletRequest request, HttpServletResponse response) {
AjaxDataEntity ajaxDataEntity = new AjaxDataEntity();
String modelId = request.getParameter("id");
RepositoryService repositoryService = activtiManager.getRepositoryService();
try {
Model modelData = repositoryService.getModel(modelId);
ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
byte[] bpmnBytes = null;
BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
bpmnBytes = new BpmnXMLConverter().convertToXML(model,"GBK");
String processName = modelData.getName() + ".bpmn20.xml";
Deployment deployment = repositoryService.createDeployment().name(modelData.getName()).addString(processName, new String(bpmnBytes)).deploy();
//修改存入部署对象ID
//Model modelData = repositoryService.createModelQuery().modelId(modelId).singleResult();
modelData.setDeploymentId(deployment.getId());
repositoryService.saveModel(modelData);
getUserContext().setInfoMessage(MessageResourcesUtils.getMessage(request, "workflow.alertinfo.deploysuccess"));
} catch (Exception e) {
e.printStackTrace();
logger.error("根据模型部署流程失败:modelId={}", modelId, e);
getUserContext().setErrorMessage(MessageResourcesUtils.getMessage(request, "workflow.alertinfo.deployfail"));
}finally {
try {
response.getWriter().write(JSONFactory.toJSONString(ajaxDataEntity, getUserContext()));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 导出model对象为指定类型
*
* @param modelId
* 模型ID
* @param type
* 导出文件类型(bpmn\json)
*/
@RequestMapping(value = "/workflow/model/export.web")
public void export(HttpServletRequest request, HttpServletResponse response) {
RepositoryService repositoryService = activtiManager.getRepositoryService();
String modelId = request.getParameter("id");
String type = "bpmn";//默认导出bpmn格式
try {
org.activiti.engine.repository.Model modelData = repositoryService.getModel(modelId);
BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
byte[] modelEditorSource = repositoryService.getModelEditorSource(modelData.getId());
JsonNode editorNode = new ObjectMapper().readTree(modelEditorSource);
BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
// 处理异常
if (bpmnModel.getMainProcess() == null) {
response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value());
response.getOutputStream().println("no main process, can't export for type: " + type);
response.flushBuffer();
return;
}
String filename = "";
byte[] exportBytes = null;
String mainProcessId = bpmnModel.getMainProcess().getId();
switch (type) {
case "bpmn": {
BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
exportBytes = xmlConverter.convertToXML(bpmnModel,"GBK");
filename = mainProcessId + ".bpmn20.xml";
break;
}
case "json": {
exportBytes = modelEditorSource;
filename = mainProcessId + ".json";
}
}
ByteArrayInputStream in = new ByteArrayInputStream(exportBytes);
IOUtils.copy(in, response.getOutputStream());
response.setHeader("Content-Disposition", "attachment; filename=" + filename);
response.flushBuffer();
} catch (Exception e) {
logger.error("导出model的xml文件失败:modelId={}, type={}", modelId, type, e);
}
}
}