• ​Spring boot 整合activiti modeler​
  • ​​前言​​
  • ​​一目录结构​​
  • ​二整合流程​
  • ​​1 复制文件​​
  • ​​2 修改文件​​
  • ​​三activiti modeler 导入现有流程文件​​


Spring boot 整合activiti modeler

前言

瞎整了一个晚上加一个上午,终于整合完成了,记录以便下次使用

Spring boot版本:1.5.3

activiti版本:5.22.0

一、目录结构


Spring boot整合activiti modeler 5.22_java

其中:

二、整合流程

2.1 复制文件

首先从官方下载activiti5.22.0版本的全代码包,并在本地解压待用

​activiti-webapp-explorer2​

  • 中复制

​src\main\java\org\activiti\explorer\servlet​

  • 路径下的文件(图中的A部分)到本地项目中(如启动后无法访问静态资源文件,则删除DispatherServletConfiguration.java这个文件)

​activiti-webapp-explorer2​

  • 中复制

​src\main\resources​

  • 路径下的

​stencilset.json​

  • 文件到本地项目的resources目录下(我这里为了管理放在了resources/activiti/modeler目录下)

​activiti-webapp-explorer2​

  • 中复制

​src\main\webapp​

  • 路径下的文件(图中C部分)到本地项目的resources/public/static目录下

​activiti-modeler​

  • 中复制

​src\main\java\org\activiti\rest\editor​

  • 路径下的文件(图中的B部分)到本地项目中

以上拷贝的路径参考上图

2.2 修改文件

为了整合能够达到效果,我们需要对以下文件进行修改

​pom.xml​​,增加如下依赖

<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-actuator</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-rest</artifactId>
<version>${activiti.version}</version>
</dependency>

<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-codec</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-css</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svg-dom</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svggen</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-explorer</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-diagram-rest</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-simple-workflow</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>${activiti.version}</version>
</dependency>

​DispatcherServletConfiguration.java​​ 

改为如下:

@Configuration
@ComponentScan({"org.activiti.rest.editor", "org.activiti.rest.diagram"})
public class DispatcherServletConfiguration {
private final Logger log = LoggerFactory.getLogger(DispatcherServletConfiguration.class);

@Bean
public SessionLocaleResolver localeResolver() {
return new SessionLocaleResolver();
}

@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
log.debug("Configuring localeChangeInterceptor");
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
return localeChangeInterceptor;
}

@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
log.debug("Creating requestMappingHandlerMapping");
RequestMappingHandlerMapping requestMappingHandlerMapping = new RequestMappingHandlerMapping();
requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
Object[] interceptors = {localeChangeInterceptor()};
requestMappingHandlerMapping.setInterceptors(interceptors);
return requestMappingHandlerMapping;
}

}

​StencilsetRestResource.java​​(如果在拷贝​​stencilset.json​​时没有放在resources目录下)

InputStream stencilsetStream = this.getClass().getClassLoader().getResourceAsStream("stencilset.json");

改为

InputStream stencilsetStream = this.getClass().getClassLoader().getResourceAsStream("activiti.modeler/stencilset.json");

​StencilsetRestResource.java​​、​​ModelEditorJsonRestResource.java​​、​​ModelSaveRestResource.java​

统一在类上加

@RequestMapping(value = "/service")

​Application.java​

在里面增加注解

@ComponentScan({"org.activiti.rest.diagram", "com.westcatr.rd"})
@EnableAsync

同时增加一个filter

@Bean
public JsonpCallbackFilter filter(){
return new JsonpCallbackFilter();
}

​editor-app​​下的​​app-cfg.js​​,将

ACTIVITI.CONFIG = {
'contextRoot' : 'activiti-webapp-explorer2/service',
};

改为

ACTIVITI.CONFIG = {
'contextRoot' : '/service',
};

​ModelSaveRestResource.java​​(主要是因为集成后保存时候报400错误) 

将代码改为如下

@RestController
@RequestMapping(value = "/service")
public class ModelSaveRestResource implements ModelDataJsonConstants {

protected static final Logger LOGGER = LoggerFactory.getLogger(ModelSaveRestResource.class);

@Autowired
private RepositoryService repositoryService;

@Autowired
private ObjectMapper objectMapper;

@RequestMapping(value="/model/{modelId}/save", method = RequestMethod.PUT)
@ResponseStatus(value = HttpStatus.OK)
public void saveModel(@PathVariable String modelId, @RequestParam("name") String name,
@RequestParam("json_xml") String json_xml, @RequestParam("svg_xml") String svg_xml,
@RequestParam("description") String description) {//对接收参数进行了修改
try {

Model model = repositoryService.getModel(modelId);

ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());

modelJson.put(MODEL_NAME, name);
modelJson.put(MODEL_DESCRIPTION, description);
model.setMetaInfo(modelJson.toString());
model.setName(name);

repositoryService.saveModel(model);

repositoryService.addModelEditorSource(model.getId(), json_xml.getBytes("utf-8"));

InputStream svgStream = new ByteArrayInputStream(svg_xml.getBytes("utf-8"));
TranscoderInput input = new TranscoderInput(svgStream);

PNGTranscoder transcoder = new PNGTranscoder();
// Setup output
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
TranscoderOutput output = new TranscoderOutput(outStream);

// Do the transformation
transcoder.transcode(input, output);
final byte[] result = outStream.toByteArray();
repositoryService.addModelEditorSourceExtra(model.getId(), result);
outStream.close();

} catch (Exception e) {
LOGGER.error("Error saving model", e);
throw new ActivitiException("Error saving model", e);
}
}
}

​activiti-modeler​​编辑关闭时返回的路径​​http://localhost:8080/static/modeler.html?modelId=1​​我们可以看到一个空白的编辑器,里面什么也木有。原因是什么呢,是因为我们的modelId=1是乱输入的,他应该要和数据库表​​ACT_RE_MODEL​​对应起来才对。那么这张表里的数据如何来的呢,我们需要自己写一个Controller,对应封装4个方法:1.新建一个空的模型;2.所有模型列表;3.发布模型;4.删除模型;(activiti已提供了保存修改和获取模型节点信息的方法,就是上面C部分的那3个类)

参考代码(该部分代码感谢:​​http://www.jianshu.com/p/cf766a713a86​​)

@RestController
@RequestMapping("models")
public class ModelerController {

@Autowired
ProcessEngine processEngine;
@Autowired
ObjectMapper objectMapper;

/**
* 新建一个空模型
* @return
* @throws UnsupportedEncodingException
*/
@PostMapping
public Object newModel() throws UnsupportedEncodingException {
RepositoryService repositoryService = processEngine.getRepositoryService();
//初始化一个空模型
Model model = repositoryService.newModel();

//设置一些默认信息
String name = "new-process";
String description = "";
int revision = 1;
String key = "process";

ObjectNode modelNode = objectMapper.createObjectNode();
modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision);

model.setName(name);
model.setKey(key);
model.setMetaInfo(modelNode.toString());

repositoryService.saveModel(model);
String id = model.getId();

//完善ModelEditorSource
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);
repositoryService.addModelEditorSource(id,editorNode.toString().getBytes("utf-8"));
return ToWeb.buildResult().redirectUrl("/modeler.html?modelId="+id);
}

/**
* 获取所有模型
* @return
*/
@GetMapping
public Object modelList(){
RepositoryService repositoryService = processEngine.getRepositoryService();
List<Model> models = repositoryService.createModelQuery().list();
return ToWeb.buildResult().putData("models", models);
}

/**
* 删除模型
* @param id
* @return
*/
@DeleteMapping("{id}")
public Object deleteModel(@PathVariable("id")String id){
RepositoryService repositoryService = processEngine.getRepositoryService();
repositoryService.deleteModel(id);
return ToWeb.buildResult().refresh();
}

/**
* 发布模型为流程定义
* @param id
* @return
* @throws Exception
*/
@PostMapping("{id}/deployment")
public Object deploy(@PathVariable("id")String id) throws Exception {

//获取模型
RepositoryService repositoryService = processEngine.getRepositoryService();
Model modelData = repositoryService.getModel(id);
byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());

if (bytes == null) {
return ToWeb.buildResult().status(Config.FAIL)
.msg("模型数据为空,请先设计流程并成功保存,再进行发布。");
}

JsonNode modelNode = new ObjectMapper().readTree(bytes);

BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
if(model.getProcesses().size()==0){
return ToWeb.buildResult().status(Config.FAIL)
.msg("数据模型不符要求,请至少设计一条主线流程。");
}
byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);

//发布流程
String processName = modelData.getName() + ".bpmn20.xml";
Deployment deployment = repositoryService.createDeployment()
.name(modelData.getName())
.addString(processName, new String(bpmnBytes, "UTF-8"))
.deploy();
modelData.setDeploymentId(deployment.getId());
repositoryService.saveModel(modelData);

return ToWeb.buildResult().refresh();
}
}

三、activiti modeler 导入现有流程文件

我们经常会使用其他的工具来制作bpmn文件,如使用eclipse的activiti designer,在我们的项目集成好activiti modeler后,就不需要再用其他工具来编辑了。那么如何将现有的bpmn文件导入到继承好的activiti modeler里呢。

​activiti-exporer​​中的​​ImportUploadReceiver.java​​里找到一些思路,我们可以新建一个上传方法,即可上传我们的现有bpmn文件。代码如下:

@RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
public void deployUploadedFile(
@RequestParam("uploadfile") MultipartFile uploadfile) {
InputStreamReader in = null;
try {
try {
boolean validFile = false;
String fileName = uploadfile.getOriginalFilename();
if (fileName.endsWith(".bpmn20.xml") || fileName.endsWith(".bpmn")) {
validFile = true;

XMLInputFactory xif = XmlUtil.createSafeXmlInputFactory();
in = new InputStreamReader(new ByteArrayInputStream(uploadfile.getBytes()), "UTF-8");
XMLStreamReader xtr = xif.createXMLStreamReader(in);
BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

if (bpmnModel.getMainProcess() == null || bpmnModel.getMainProcess().getId() == null) {
// notificationManager.showErrorNotification(Messages.MODEL_IMPORT_FAILED,
// i18nManager.getMessage(Messages.MODEL_IMPORT_INVALID_BPMN_EXPLANATION));
System.out.println("err1");
} else {

if (bpmnModel.getLocationMap().isEmpty()) {
// notificationManager.showErrorNotification(Messages.MODEL_IMPORT_INVALID_BPMNDI,
// i18nManager.getMessage(Messages.MODEL_IMPORT_INVALID_BPMNDI_EXPLANATION));
System.out.println("err2");
} else {

String processName = null;
if (StringUtils.isNotEmpty(bpmnModel.getMainProcess().getName())) {
processName = bpmnModel.getMainProcess().getName();
} else {
processName = bpmnModel.getMainProcess().getId();
}
Model modelData;
modelData = repositoryService.newModel();
ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processName);
modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
modelData.setMetaInfo(modelObjectNode.toString());
modelData.setName(processName);

repositoryService.saveModel(modelData);

BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
ObjectNode editorNode = jsonConverter.convertToJson(bpmnModel);

repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));
}
}
} else {
// notificationManager.showErrorNotification(Messages.MODEL_IMPORT_INVALID_FILE,
// i18nManager.getMessage(Messages.MODEL_IMPORT_INVALID_FILE_EXPLANATION));
System.out.println("err3");
}
} catch (Exception e) {
String errorMsg = e.getMessage().replace(System.getProperty("line.separator"), "<br/>");
// notificationManager.showErrorNotification(Messages.MODEL_IMPORT_FAILED, errorMsg);
System.out.println("err4");
}
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
// notificationManager.showErrorNotification("Server-side error", e.getMessage());
System.out.println("err5");
}
}
}
}

补充:上面的是之前做项目整合时的流程记录,看到有朋友说不能够整合,今天按照流程重新整合了一遍,并上传了代码