SpringCloud&SpringBoot集成Activiti6.0

今天小编就在这里针对于SpringCloud或SpringBoot集成Activiti中遇到的坑与解决办法做个吐槽。

顺便把SpringCloud的基础集成也做个说明
小编目前用的开发环境是IDEA2018的jdk用的是1.8的,前面如何搭建一个maven环境就不再做啰嗦了,直接撸代码!

一:在MAVEN项目中配置pom.xml

SpringCloud需要的几个基础包

SpringBoot与SpringCloud的合适的版本对应关系

SpringBootSpringCloud
1.2.xAngel版本
1.3.xBrixton版本
1.4.xstripes Camden版本
1.5.xDalston版本、Edgware版本
2.0.xFinchley版本
2.1.xGreenwich.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文件替换掉就可以了

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);
    }
  }
}

重启,创建,编写流程,保存,完美结束,此次分享就到此结束了,如果感觉有问题的大神请多多指教!

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值