SpringCloud&SpringBoot集成Activiti6.0
今天小编就在这里针对于SpringCloud或SpringBoot集成Activiti中遇到的坑与解决办法做个吐槽。
顺便把SpringCloud的基础集成也做个说明
小编目前用的开发环境是IDEA2018的jdk用的是1.8的,前面如何搭建一个maven环境就不再做啰嗦了,直接撸代码!
一:在MAVEN项目中配置pom.xml
SpringCloud需要的几个基础包
SpringBoot与SpringCloud的合适的版本对应关系
SpringBoot | SpringCloud |
---|---|
1.2.x | Angel版本 |
1.3.x | Brixton版本 |
1.4.x | stripes Camden版本 |
1.5.x | Dalston版本、Edgware版本 |
2.0.x | Finchley版本 |
2.1.x | Greenwich.SR2 |
具体的详细版本可以参考官方提供的对应表 | |
Cloud JSON体方式详细版本地址:https://start.spring.io/actuator/info | |
Cloud官方地址:https://start.spring.io/actuator/info |
SpringBoot基础启动包,小编目前用的是当前比较新的版本
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/>
</parent>
SpringCloud的主要包,这里也是用的比较新的版本,注意这个版本对应关系一定要与spring-boot-starter-parent的包对应好,否则有各种兼容性问题
配置包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
监控系统健康情况的工具
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
顾名思义是SpringBoot中集成的各种测试工具
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
对Web容器进行快速处理的包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
以下是要对集成的Activity几个必要与常用的包进行集成
Activiti 启动器,这里我们是用的6.0.0的版本进行管理
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>6.0.0</version>
</dependency>
下面这几个Activiti的包是他的一些流程上的工具
在线设计器
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-modeler</artifactId>
<version>5.22.0</version>
<exclusions>
<exclusion>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
</exclusion>
</exclusions>
</dependency>
动态流程图
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-diagram-rest</artifactId>
<version>5.22.0</version>
</dependency>
好了后面就不废话了,其他的需要增加一些mysql与http之类的工具包依照个人喜好就可以,下面是完整的pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 用来设置版本号 -->
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.RC2</spring-cloud.version>
<activiti.version>6.0.0</activiti.version>
<druid.version>1.1.21</druid.version>
<activiti-modeler.version>5.22.0</activiti-modeler.version>
<httpclient.version>4.5.10</httpclient.version>
<fastjson.version>1.2.62</fastjson.version>
<pagehelper.version>1.2.12</pagehelper.version>
</properties>
<groupId>com.acti</groupId>
<artifactId>activity</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Activiti 启动器-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- Activiti 在线设计 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-modeler</artifactId>
<version>${activiti-modeler.version}</version>
<exclusions>
<exclusion>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Activiti 流程图 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-diagram-rest</artifactId>
<version>${activiti-modeler.version}</version>
</dependency>
<!-- druid阿里巴巴数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--mybatis-starter-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!--mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
创建启动类
启动类这里我要啰嗦一下了,正常的情况下抛出Activiti的情况下启动类直接通过注解
@SpringBootApplication
声明就可以了,为什么要加入一个exclude
排除呢?
因为在集成Activiti的时候Activiti包中自带了一个Security拦截器而在SpringBoot中的org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
包里也是存在一个同样的Security,所以会导致包冲突,要做相应的排除,
package com.acti;
import org.activiti.spring.boot.SecurityAutoConfiguration;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Desc activity
* @Author jx111
* @Date 2019/11/29-16:28
*/
@SpringBootApplication(exclude = { SecurityAutoConfiguration.class})
public class ActivityApplication {
public static void main(String[] args) {
SpringApplication.run(ActivityApplication.class, args);
}
}
创建配置文件
这里面小编用的数据库管理是用的阿里的druid进行操作的,这里注意的是driver-class-name,这个属性要根据boot的版本而决定数据库驱动的名称,可以网上查一查对应的驱动版本,小编的驱动版本比较新是com.mysql.cj.jdbc.Driver
的,低版本的应该是com.mysql.jdbc.Driver
,配置文件就不做过多的说明了
server:
port: 8888
spring:
application:
name: activity
datasource:
name: test
url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/activity?useUnicode=true&characterEncoding=UTF8&useSSL=false
username: activity
password: activity
# 使用druid数据源
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
activiti:
#关闭activiti自动部署(使用流程设计器部署,不使用具体文件访问方式)
check-process-definitions: false
#设置流程引擎启动和关闭时数据库执行的策略
#false:false为默认值,设置为该值后,Activiti在启动时,会对比数据库表中保存的版本,如果版本不匹配时,将在启动时抛出异常。
#true:设置为该值后,Activiti会对数据库中所有的表进行更新,如果表不存在,则Activiti会自动创建。
#create-drop:Activiti启动时,会执行数据库表的创建操作,在Activiti关闭时,执行数据库表的删除操作。
#drop-create:Activiti启动时,执行数据库表的删除操作在Activiti关闭时,会执行数据库表的创建操作。
database-schema-update: true
#保存历史数据级别设置为full最高级别,便于历史数据的追溯
#none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。
#activity:级别高于none,保存流程实例与流程行为,其他数据不保存。
#audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性。audit为history的默认值。
#full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等。
history-level: full
management:
endpoints:
web:
base-path: /
logging:
level:
com.acti: DEBUG
第一次启动
启动成功,如果出现启动失败的情况提示数据库中表不存在的错误则在jdbc的连接url中增加如下属性
nullCatalogMeansCurrent=true
完整连接
url: jdbc:mysql:// M Y S Q L H O S T : l o c a l h o s t : {MYSQL_HOST:localhost}: MYSQLHOST:localhost:{MYSQL_PORT:3306}/activity?useUnicode=true&characterEncoding=UTF8&useSSL=false&nullCatalogMeansCurrent=true
小编也是个佛系的人,附上佛系代码,保存成banner.txt
文件放在resources资源目录的根部就可以了
${AnsiColor.BLUE}${AnsiStyle.ITALIC}
${AnsiStyle.NORMAL}${AnsiColor.DEFAULT}
${AnsiColor.YELLOW}${AnsiStyle.ITALIC}
..........................我佛慈悲..........................
_ooOoo_
o8888888o
88" . "88
(| ^_^ |)
O\ = /O
____/`---'\____
.' \\| |// `.
/ \\||| : |||// \
/ _||||| -卍- |||||- \
| | \\\ - /// | |
| \_| ''\---/'' | |
\ .-\__ `-` ___/-. /
___`. .' /--.--\ `. . ___
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;`\ _ /`;.`/ - ` : | |
\ \ `-. \_ __\ /__ _/ .-` / /
========`-.____`-.___\_____/___.-`____.-'========
`=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
佛祖保佑 永无BUG 永不修改
${AnsiStyle.NORMAL}${AnsiColor.DEFAULT}
日志中出现了一下create.*.sql的执行操作,咱们看一下数据库,神奇的多了一些表,这是因为咱们配置的activiti的配置属性中有一个database-schema-update: true
配置中已经做了说明,这些都是activiti自动生成的表
二:集成activiti可操作流程
流程界面样式
目前基础的启动已经完成了,接下来继续集成activiti可操作流程界面
下载资源
下载activiti5.22.0modeler的源码https://github.com/Activiti/Activiti/tree/activiti-5.22.0
复制Activiti 5.22.0文件中的Activiti\modules\activiti-webapp-explorer2\src\main\webapp中的diagram-viewer、editor-app两个文件夹和modeler.html文件到Spring Boot项目resources目录下的static文件夹下。
找到项目中resources下的static/editor-app/app-cfg.js文件
修改app-cfg.js文件中contextRoot的指向路径,根据自己项目而定,小编的是直接/service
var ACTIVITI = ACTIVITI || {};
ACTIVITI.CONFIG = {
'contextRoot' : '/service',
};
找到项目中resources根路径下的modeler.html
modeler.html中有一段Activiti模板的头样式logo标题栏,可以设定成自己的,根据实际需求修改就可以。小编直接隐藏掉了,在div标签上增加了
style="display:none"
<div style="display:none" class="navbar navbar-fixed-top navbar-inverse" role="navigation" id="main-header">
<div class="navbar-header">
<a href="" ng-click="backToLanding()" class="navbar-brand"
title="{{'GENERAL.MAIN-TITLE' | translate}}"><span
class="sr-only">{{'GENERAL.MAIN-TITLE' | translate}}</span></a>
</div>
</div>
找到项目中resources下的static/editor-app/css/style-common.css文件
修改style-common.css文件下的
wrapper.full
样式节点下的padding
第一个属性也就是top改为0px
.wrapper.full {
padding: 0px 0px 0px 0px;
overflow: hidden;
max-width: 100%;
min-width: 100%;
}
接下来我们在去刚下载的activiti5.22.0资源文件下找到
stencilset.json
文件,这个文件是配置Activiti流程内容的相关配置,activiti自带的模板都是英文的。
流程界面应用服务
前端配置的样式与页面基本集成的差不多了,接下来集成后端逻辑
第一步我们再去activiti5.22.0资源文件中找到如下三个java文件:
(1)Activiti-activiti-5.22.0\modules\activiti-modeler\src\main\java\org\activiti\rest\editor\main\StencilsetRestResource.java
(2)F:\sys_activity\Activiti-activiti-5.22.0\modules\activiti-modeler\src\main\java\org\activiti\rest\editor\model\ModelEditorJsonRestResource.java
(3)F:\sys_activity\Activiti-activiti-5.22.0\modules\activiti-modeler\src\main\java\org\activiti\rest\editor\model\ModelSaveRestResource.java
将这三个java文件放在自己的项目当中,可以放在controller目录下,小编创建了一个目录是editor文件夹
修改这三个文件,在代码头部增加@RequestMapping(“/service”),这个要根据咱们刚刚修改的app-cfg.js指向的路径地址保持一致
ModelEditorJsonRestResource.java
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* @author Tijs Rademakers
*/
@RestController
@RequestMapping("/service")
public class ModelEditorJsonRestResource implements ModelDataJsonConstants {
protected static final Logger LOGGER = LoggerFactory.getLogger(ModelEditorJsonRestResource.class);
@Autowired
private RepositoryService repositoryService;
@Autowired
private ObjectMapper objectMapper;
@RequestMapping(value="/model/{modelId}/json", method = RequestMethod.GET, produces = "application/json")
public ObjectNode getEditorJson(@PathVariable String modelId) {
ObjectNode modelNode = null;
Model model = repositoryService.getModel(modelId);
if (model != null) {
try {
if (StringUtils.isNotEmpty(model.getMetaInfo())) {
modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
} else {
modelNode = objectMapper.createObjectNode();
modelNode.put(MODEL_NAME, model.getName());
}
modelNode.put(MODEL_ID, model.getId());
ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(
new String(repositoryService.getModelEditorSource(model.getId()), "utf-8"));
modelNode.put("model", editorJsonNode);
} catch (Exception e) {
LOGGER.error("Error creating model JSON", e);
throw new ActivitiException("Error creating model JSON", e);
}
}
return modelNode;
}
}
ModelSaveRestResource.java
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* @author Tijs Rademakers
*/
@RestController
@RequestMapping("/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, @RequestBody MultiValueMap<String, String> values) {
try {
Model model = repositoryService.getModel(modelId);
ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
modelJson.put(MODEL_NAME, values.getFirst("name"));
modelJson.put(MODEL_DESCRIPTION, values.getFirst("description"));
model.setMetaInfo(modelJson.toString());
model.setName(values.getFirst("name"));
repositoryService.saveModel(model);
repositoryService.addModelEditorSource(model.getId(), values.getFirst("json_xml").getBytes("utf-8"));
InputStream svgStream = new ByteArrayInputStream(values.getFirst("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);
}
}
}
StencilsetRestResource.java
import java.io.InputStream;
import org.activiti.engine.ActivitiException;
import org.apache.commons.io.IOUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Tijs Rademakers
*/
@RestController
@RequestMapping("/service")
public class StencilsetRestResource {
@RequestMapping(value="/editor/stencilset", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
public @ResponseBody String getStencilset() {
InputStream stencilsetStream = this.getClass().getClassLoader().getResourceAsStream("stencilset.json");
try {
return IOUtils.toString(stencilsetStream, "utf-8");
} catch (Exception e) {
throw new ActivitiException("Error while loading stencil set", e);
}
}
}
好了目前Activiti的基本功能算是集成了,接下来咱们验证一下
创建一个启动Activiti模板的controller去把这个模板页面吊起来,咱们在项目中创建一个controller文件里面写上调用类,小编起名为CreateModelController
CreateModelController.java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Desc activity
* @Author jx111
* @Date 2019/12/2-14:21
*/
@RestController
public class CreateModelController {
protected static final Logger LOGGER = LoggerFactory.getLogger(CreateModelController.class);
@Autowired
private ObjectMapper objectMapper;
@Autowired
private RepositoryService repositoryService;
/**
* 新建一个空模型
* @return
* @throws
*/
@GetMapping("/create")
public void newModel(HttpServletRequest request, HttpServletResponse response) throws Exception {
//初始化一个空模型
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.set("stencilset", stencilSetNode);
repositoryService.addModelEditorSource(id,editorNode.toString().getBytes("utf-8"));
response.sendRedirect("/modeler.html?modelId="+id);
}
}
第二次启动
浏览器访问http://localhost:8011/create,触发咱们之前写的调起模板的方法,莫名其妙的问题来了,居然出现了一个登陆页面
小编刚开始有些诧异,后来才知道是Security拦截器搞的鬼,把请求做了拦截,咱们先分析一下,如果有登录页面的话应该如何进入系统呢,用户名密码在哪里呢?
后来查了一下官方的api得知第一次Activiti初始化的用户名为user
而密码就是刚在启动日志中有一串莫名其名的一串码值
填入用户名密码,然后登陆,当出现这个的时候 证明你的集成已经完毕了
但是作为一个中国人,应该有爱国精神,所以小编决定把他汉化了,小编在此提供汉化文件,
stencilset.json
中文版,把resources跟下的stencilset.json文件替换掉就可以了
启动看一下效果
三:去除拦截器中的过滤器
前期工作已经做好了,但是这东西真正集成在系统中肯定是不可能多一层登录的,接下来咱们想办法吧这个登录去除。
之前我们说过Security拦截器activiti里面有一个SpringBoot中的autoconfigure下还有一个,我们之前是已经把activiti中的在启动的时候已经忽略了,那咱们把另一个也忽略了不就行了,第一次尝试,启动类中在忽略包里面加上SpringBoot的autoconfigure中的SecurityAutoConfiguration
import org.activiti.spring.boot.SecurityAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(exclude = { SecurityAutoConfiguration.class,org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
public class StringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(StringBootDemoApplication.class, args);
}
}
启动项目看效果,居然不生效,登录页面依然存在,查了好多的资料,也参考了别的博主的做法,大同小异都是这么做的,但是为什么不好使呢?考虑是否是SpringBoot与Cloud版本的问题,决定降一下版本试一下
小编把SpringBoot版本降低到了2.0.1.RELEASE,SpringCloud的版本降低到了Finchley.RELEASE
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
启动的时候报了个错,这个错误很明显低版本的mysql的驱动包不是这么定义的
修改application.yml中数据源配置,把com.mysql.cj.jdbc.Driver修改为com.mysql.jdbc.Driver
再次启动,发现登录页面没有了,请求也没有被拦截了,控制台也没有在打印出那串密码了,居然可以了。这个问题,小编没有找到具体是什么原因导致的,由于还有任务在身,时间紧迫,如果有知道原因的各位大神,还请多多指教!
这个问题当然这样的处理方式显得很不舒服,小编决定换回原来的版本解决此问题
出现问题的罪魁祸首肯定是定位在Security上面了,经过小编在网络的海洋上一顿浏览,最终找到了Security里面的WebSecurityConfigurerAdapter拦截器是可以被重写的,小编就在这里下手!
在项目路径下创建config文件做相应配置性管理与controller路径持平就好,在下面创建ActuatorWebSecurity类用作准备拦截Security请求使用
ActuatorWebSecurity.java
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @Desc activity
* @Author jx111
* @Date 2019/12/2-17:19
*/
@Configuration
@EnableWebSecurity
public class ActuatorWebSecurity extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//普通的接口不需要校验
.antMatchers("**").permitAll()
// swagger页面需要添加登录校验
//.antMatchers("/modeler.html").authenticated()
.and()
//关闭CSRF保护
.csrf().disable()
.formLogin();
}
}
最后启动验证,虽然security的密码还是在控制台生成了,也没有真正的关闭security,但是问题还是得以解决了,如果有知道真想的大佬还请多多指教,毕竟这种操作不太常规
为了防止activity出现字体乱码增加的工具类
是在config目录下创建ActivitiConfig工具类
ActivitiConfig.java
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
/**
* @Desc activity
* @Author jx111
* @Date 2019/11/29-17:44
*/
@Controller
@Configuration
public class ActivitiConfig implements ProcessEngineConfigurationConfigurer {
@Override
public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {
/**设置字体防止中文乱码**/
springProcessEngineConfiguration.setActivityFontName("宋体");
springProcessEngineConfiguration.setLabelFontName("宋体");
springProcessEngineConfiguration.setAnnotationFontName("宋体");
}
}
保存我们的第一个流程
我们简单创建一个流程试一下,以下是小编创建的流程
我们点击左上方的保存试一下
真是好事多磨呀,当点击保存的时候出现Unexpected error: could not save model
后台出现了一个这个错误,经查证,保存的时候调用的是咱们在模板中拿过来的ModelSaveRestResource
类中的saveModel
方法,是因为后台收到的MultiValueMap值为空导致的
2019-12-06 14:16:04.599 WARN 14508 --- [nio-8011-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public void com.javacyun.StringBootDemo.editor.ModelSaveRestResource.saveModel(java.lang.String,org.springframework.util.MultiValueMap<java.lang.String, java.lang.String>)]
解决方法可以修改
ModelSaveRestResource
类中的方法变成这种接受参数的方式,问题解决,可以保存
@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);
}
}
这个问题经小编分析了一下,原来方法体接收方法的method
方式是RequestMethod.PUT
,而前台调用的时候ajax提交Content-Type是application/x-www-form-urlencoded而method用的是PUT类型,
找到的问题的根本原因解决方式有两种
第一种是吧ajax提交方式稍微修改
'Content-Type': 'application/x-www-form-urlencoded;
修改为’Content-Type': 'application/json
;
$http({ method: 'PUT',
data: params,
ignoreErrors: true,
headers: {'Accept': 'application/json',
'Content-Type': 'application/json; charset=UTF-8'},
transformRequest: function (obj) {
var str = [];
for (var p in obj) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
return str.join("&");
},
url: KISBPM.URL.putModel(modelMetaData.modelId)})
.success(function (data, status, headers, config) {
$scope.editor.handleEvents({
type: ORYX.CONFIG.EVENT_SAVED
});
$scope.modelData.name = $scope.saveDialog.name;
$scope.modelData.lastUpdated = data.lastUpdated;
$scope.status.loading = false;
$scope.$hide();
// Fire event to all who is listening
var saveEvent = {
type: KISBPM.eventBus.EVENT_TYPE_MODEL_SAVED,
model: params,
modelId: modelMetaData.modelId,
eventType: 'update-model'
};
KISBPM.eventBus.dispatch(KISBPM.eventBus.EVENT_TYPE_MODEL_SAVED, saveEvent);
// Reset state
$scope.error = undefined;
$scope.status.loading = false;
// Execute any callback
if (successCallback) {
successCallback();
}
})
.error(function (data, status, headers, config) {
$scope.error = {};
console.log('Something went wrong when updating the process model:' + JSON.stringify(data));
$scope.status.loading = false;
});
第二种方式吧请求method: 'PUT’修改为method: ‘POST’
$http({ method: 'POST',
data: params,
ignoreErrors: true,
headers: {'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
transformRequest: function (obj) {
var str = [];
for (var p in obj) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
return str.join("&");
},
url: KISBPM.URL.putModel(modelMetaData.modelId)})
.success(function (data, status, headers, config) {
$scope.editor.handleEvents({
type: ORYX.CONFIG.EVENT_SAVED
});
$scope.modelData.name = $scope.saveDialog.name;
$scope.modelData.lastUpdated = data.lastUpdated;
$scope.status.loading = false;
$scope.$hide();
// Fire event to all who is listening
var saveEvent = {
type: KISBPM.eventBus.EVENT_TYPE_MODEL_SAVED,
model: params,
modelId: modelMetaData.modelId,
eventType: 'update-model'
};
KISBPM.eventBus.dispatch(KISBPM.eventBus.EVENT_TYPE_MODEL_SAVED, saveEvent);
// Reset state
$scope.error = undefined;
$scope.status.loading = false;
// Execute any callback
if (successCallback) {
successCallback();
}
})
.error(function (data, status, headers, config) {
$scope.error = {};
console.log('Something went wrong when updating the process model:' + JSON.stringify(data));
$scope.status.loading = false;
});
后台接收方法也相应修改为 method = RequestMethod.POST
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* @author Tijs Rademakers
*/
@RestController
@RequestMapping("/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.POST)
@ResponseStatus(value = HttpStatus.OK)
public void saveModel(@PathVariable String modelId, @RequestBody MultiValueMap<String, String> values) {
try {
Model model = repositoryService.getModel(modelId);
ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
modelJson.put(MODEL_NAME, values.getFirst("name"));
modelJson.put(MODEL_DESCRIPTION, values.getFirst("description"));
model.setMetaInfo(modelJson.toString());
model.setName(values.getFirst("name"));
repositoryService.saveModel(model);
repositoryService.addModelEditorSource(model.getId(), values.getFirst("json_xml").getBytes("utf-8"));
InputStream svgStream = new ByteArrayInputStream(values.getFirst("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);
}
}
}
重启,创建,编写流程,保存,完美结束,此次分享就到此结束了,如果感觉有问题的大神请多多指教!