前言
本文的目标,是在Spring (SpringBoot)项目中,集成Flowable 工作流引擎和Flowable流程编辑器。 通过流程编辑器编辑并发布流程,在Spring项目中使用。
Flowable 是一个基于Apache 2.0协议,开源,并且有着大量社区讨论与更新的工作流引擎。
春节疫情宅家的时候,刚发布了最新的6.5.0版本。本文中使用次新版本6.4.2。
使用工作流引擎,在处理复杂流程任务,例如订单、报价、采购处理、合同审核、请假申请时,能给各方人员带来好处。
工作流让开发人员不再需要特别关注流程流转,从而能更专注与业务实现,减少硬性编码,更利于维护和扩展。
工作流让流程应用使用人员,能更清晰地审视,管理流程,更灵活,快速地修改流程,从而另企业办公更加自动化与规范化,提升企业的管理效率,合规性与风控水平。
Flowable官方Github: github.com/flowable
官方网站: flowable.com/
注意,Flowable有开源与商用版的区别,商用的功能文档都要多得多,当然,它是要收费的。
Flowable开源社区版本在 flowable.com/open-source…
Flowable简介
主要组成部分
- BPMN(Business Process Modeling Notation)引擎 - 业务流程模型引擎采用bpmn2.0标准的流程引擎。
- CMN 决策引擎,基于 PMML/DMN标准的规则引擎,配合流程中的“决策任务”使用 。
- CMMN 案例引擎,CMMN标准,与流程引擎相比,更适用于动态、自由、并行的情况。
- Form 表单引擎,管理表单, 表单信息由json表达,可绑定到流程的节点中使用。
官方提供的4 个示例应用
Flowable-UI-Modeler
模型编辑器,能编辑 流程 / 决策表 / 表单 编辑器
Flowable-UI-Task
操作与管理flowable 任务的UI 应用,用户可以通过它对Flowable任务进行 查看/接收/完成 等操作。
Flowbale-UI-Admin
管理员权限的Flowable用户通过这个来查询和修改一些流程/规则/表单
Flowable-UI-IDM
用户等身份认证与权限管理,Flowable IDM的图形界面应用
要想简单快速地运行体验一把Flowable, 可以使用官方提供的All in One docker image
Spring项目与Flowable的集成
假设我们的集成是这样的:
现有一个Spring项目,它有自己的用户,业务等。它是一个单独进程,在下文中称作Spring项目。
Flowable UI Modeler 作为另一个单独的进程,下文中或称为Modeler。
两者通过Rest方式通信,Modeler为Spring项目提供流程建模服务,使用Spring项目提供的用户身份权限认证接口。Spring项目中集成Flowable 引擎核心,使用Modeler编辑发布过来的流程模型完成工作流。
第一步:创建启动SpringBoot项目,集成Flowable引擎核心
这里使用低码平台DaaS生成了一个简单的SpringBoot项目。
DaaS是成都双链科技(对,就是我公司)自主研发的低码开发平台,它可以帮助咨询人员快速生成可运行产品原型,让程序员更专注于业务,让产品开发迭代更加快速,规范,产业化。具体请见示例项目
以及客户端工具
这个项目暂时不带任何实际业务,只用于与Flowable集成测试,它具有以下3个功能:
1. 提供了一个账号+密码的登录认证接口给Flowable-UI-Modeler使用
DaaS生成的项目中,自带用户身份认证管理,对外提供账号密码登录接口大致如下,这个就是平时项目里的登录接口,发送用户名密码,登录成功返回用户信息:HTTP POST:
http://localhost:8880/flowable/secUserManager/login/username/password/
Request:
{
"username":"XXXXXX",
"password":"XXXXXX"
}
Response:
{
......
"displayName": "张三",
"id": "SU000001",
"login": "User000001",
"mobile": "139****0001",
"email": "1000001@qq.com",
"pwd": "**********",
......
}在gradle中加入Flowable提供的SpringBoot Starter即可
compile 'org.flowable:flowable-spring-boot-starter:6.4.2'3. 向Flowable-UI-Modeler提供流程定义发布接口在gradle中加入Flowable-app-rest依赖
compile 'org.flowable:flowable-app-rest:6.4.2在SpringBoot启动类并引入Flowable-app-rest模块中的org.flowable.app.rest.service.api.repository.AppDeploymentCollectionResource,向Flowable-UI-Modeler提供流程定义发布接口
@Import(org.flowable.app.rest.service.api.repository.AppDeploymentCollectionResource.class)4. 去掉Flowable的Spring Security配置以免干扰Spring项目原来自己的安全策略
PS: 实际上这里我去掉了所有Spring Security配置,单纯去掉Flowble的,只需要去掉FlowableSecurityAutoConfiguration
@SpringBootApplication(exclude = { SecurityAutoConfiguration.class,
FlowableSecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class })5. 启动应用
最终,Spring项目完整的gradle配置文件如下,请注意配置文件中都注释,有部分依赖是DaaS自带的依赖,并非Flowable必须的
buildscript {
repositories {
maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.6.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group 'com.doublechaintech'
version '1.0.0'
[compileJava,compileTestJava,javadoc]*.options*.encoding = "UTF-8"
[compileJava,compileTestJava]*.options*.compilerArgs = ["-parameters"]
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceSets {
main {
output.resourcesDir = file('WEB-INF/classes')
java.outputDir = file('WEB-INF/classes')
java { srcDirs = srcFolders.split(",").toList();}
resources {srcDirs = srcFolders.split(",").toList(); }
}
test {
java {
srcDirs = ['WEB-INF/unittesting']
}
resources {
srcDirs = ['WEB-INF/unittesting']
}
}
}
repositories {
mavenLocal()
maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
jcenter()
mavenCentral()
}
dependencies {
// DaaS项目自带依赖
compile group: 'com.auth0', name: 'java-jwt', version: '3.4.0'
compile group: 'com.aliyun', name: 'aliyun-java-sdk-core', version: '3.5.1'
compile group: 'com.aliyun', name: 'aliyun-java-sdk-dysmsapi', version: '1.0.0'
compile group: 'com.aliyun', name: 'aliyun-java-sdk-sts', version: '3.0.0'
compile group: 'com.aliyun.mns', name: 'aliyun-sdk-mns', version: '1.1.8'
compile group: 'com.aliyun.oss', name: 'aliyun-sdk-oss', version: '2.8.3'
compile group: 'com.github.binarywang', name: 'weixin-java-miniapp', version: '3.5.0'
compile group: 'com.github.binarywang', name: 'weixin-java-pay', version: '3.5.0'
compile group: 'com.github.binarywang', name: 'weixin-java-cp', version: '3.5.0'
compile group: 'javax.mail', name: 'mail', version: '1.4.7'
compile group: 'mysql', name: 'mysql-connector-java'
compile group: 'com.h2database', name: 'h2', version: '1.4.200'
compile group: 'org.apache.kafka', name: 'kafka-clients', version: '1.1.0'
compile group: 'com.zaxxer', name: 'HikariCP', version: '3.3.1'
compile group: 'org.elasticsearch.client', name: 'elasticsearch-rest-high-level-client', version: '5.6.3'
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-jdbc")
compile 'com.qiniu:qiniu-java-sdk:7.2.+'
compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.14.4'
compile group: 'redis.clients', name: 'jedis', version: '2.9.0'
compile group: 'org.apache.poi', name: 'poi-ooxml', version: '4.0.1'
testCompile group: 'junit', name: 'junit', version: '4.12'
// Fowable相关依赖
compile 'org.flowable:flowable-spring-boot-starter:6.4.2'
compile ('org.flowable:flowable-app-rest:6.4.2'){
exclude group: 'org.slf4j'
}
compile group: 'org.flowable', name: 'flowable-form-model', version: '6.4.2'
annotationProcessor 'org.projectlombok:lombok:1.18.2'
compileOnly 'org.projectlombok:lombok:1.18.2'
}完整启动类如下,
package com.skynet.bootstrap;
import org.flowable.app.rest.AppRestResponseFactory;
import org.flowable.spring.boot.FlowableSecurityAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
import com.doublechain.flowable.CustomUCInvocationServlet;
@Import(org.flowable.app.rest.service.api.repository.AppDeploymentCollectionResource.class)
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, RestClientAutoConfiguration.class, KafkaAutoConfiguration.class, SecurityAutoConfiguration.class,
FlowableSecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class })
@ImportResource(locations = { "classpath:/META-INF/spring.xml", "classpath:/META-INF/online-system.xml" })
@ServletComponentScan(basePackageClasses = { CustomUCInvocationServlet.class })
public class AppEntrance {
public static void main(String[] args) {
SpringApplication.run(AppEntrance.class, args);
}
@Bean
public AppRestResponseFactory restResponseFactory() {
return new AppRestResponseFactory();
}
}
启动之后,可以从日志看到,Flowable-spring-boot-starter利用Liquibase, 自动创建了Flowable的相关数据库表。至此,我们就启动了一个引入Flowable的SpringBoot项目
第二步:启动Flowable UI Modeler
这里,我们通过引入Maven Repository里现成的依赖,来启动一个Flowable-UI-Modeler
1. 通过Spring Boot Initializer start.spring.io/来初始化一个Spring Boot项目,啥依赖都不加,生成一个简单SpringBoot项目
2. 在build.gradle中加入Flowable-UI-Modeler的依赖
implementation 'org.flowable:flowable-ui-modeler-app:6.4.2'
PS:吐槽一下,这个应该弄个Starter啊
3. 从Flowable-UI-Modeler源码中拷贝配置文件内容,加入到我们的配置文件里(application.properties),完整内容如下,其中重要的部分请参见中文注释
# JDBC Driver class
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# JDBC连接字符串
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/flowable_ui_modeler?characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
# 数据库账号密码
spring.datasource.username=root
spring.datasource.password=hahahaha
# 允许覆盖Bean定义
spring.main.allow-bean-definition-overriding=true
spring.mvc.static-path-pattern=/**
# SpringMVC静态资源路径
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,\
classpath:/static/,classpath:/public/,classpath:/WEB-INF/classes/static/
server.servlet.context-path=/flowable-modeler
management.endpoints.jmx.unique-names=true
# This is needed to force use of JDK proxies instead of using CGLIB
spring.aop.proxy-target-class=false
spring.aop.auto=false
spring.application.name=flowable-ui-modeler
#
# SECURITY
#
spring.security.filter.dispatcher-types=REQUEST,FORWARD,ASYNC
spring.liquibase.enabled=false
spring.banner.location=classpath:/org/flowable/spring/boot/flowable-banner.txt
# The default domain for generating ObjectNames must be specified. Otherwise when multiple Spring Boot applications start in the same servlet container
# all would be created with the same name (com.zaxxer.hikari:name=dataSource,type=HikariDataSource) for example
spring.jmx.default-domain=${spring.application.name}
# Expose all actuator endpoints to the web
# They are exposed, but only authenticated users can see /info and /health abd users with access-admin can see the others
management.endpoints.web.exposure.include=*
# Full health details should only be displayed when a user is authorized
management.endpoint.health.show-details=when_authorized
# Only users with role access-admin can access full health details
management.endpoint.health.roles=access-admin
# Spring prefixes the roles with ROLE_. However, Flowable does not have that concept yet, so we need to override that with an empty string
flowable.common.app.role-prefix=
spring.datasource.hikari.poolName=${spring.application.name}
# 10 minutes
spring.datasource.hikari.maxLifetime=600000
# 5 minutes
spring.datasource.hikari.idleTimeout=300000
spring.datasource.hikari.minimumIdle=5
spring.datasource.hikari.maximumPoolSize=50
# test query for H2, MySQL, PostgreSQL and Microsoft SQL Server
#spring.datasource.hikari.connection-test-query=select 1
# test query for Oracle
#spring.datasource.hikari.connection-test-query=SELECT 1 FROM DUAL
# test query for DB2
#spring.datasource.hikari.connection-test-query=SELECT current date FROM sysibm.sysdummy1
# The maximum file upload limit. Set to -1 to set to 'no limit'. Expressed in bytes
spring.servlet.multipart.max-file-size=10MB
flowable.common.app.idm-url=http://localhost:8880/flowable-idm
flowable.common.app.idm-admin.user=SU000001
flowable.common.app.idm-admin.password=admin123
flowable.modeler.app.deployment-api-url=http://localhost:8880/flowable/dispatch
# Rest API
flowable.modeler.app.rest-enabled=true
# Configures the way user credentials are verified when doing a REST API call:
# 'any-user' : the user needs to exist and the password need to match. Any user is allowed to do the call (this is the pre 6.3.0 behavior)
# 'verify-privilege' : the user needs to exist, the password needs to match and the user needs to have the 'rest-api' privilege
# If nothing set, defaults to 'verify-privilege'
flowable.rest.app.authentication-mode=verify-privilege
server.port=8888
# 认证接口
auth.url=http://localhost:8880/flowable/secUserManager/login/username/password/
4. 改造Spring Security部分,使用自己SpringBoot项目提供的认证接口
根据Flowable官方说明,启动Flowable UI Modeler,或者说启动任何一个Flowable 应用时,必须同时启动Flowable IDM (只是Flowable IDM 不是 Flowable-UI-IDM. Flowable-UI-IDM是Flowable IDM + 图形界面 ),为Flowable应用提供身份认证服务。
Flowable UI Modeler中,关于安全的配置在配置类
org.flowable.ui.modeler.conf.SecurityConfiguration
当同时启动Flowale-UI-Modeler和Flowable IDM时,关于身份和权限认证这一块儿,大概是这样的:
从上图,与SecurityConfiguration中,可以看到,有以下几个地方需要改动:
在最前面有一个FlowableCookieFilter, 它做的事情用伪代码大概表现为这样的:
if(不允许跳过){
if( 存在名为FLOWABLE_REMEMBER_ME的Cookie ){
以其值作参数向IDM查询获得一个RemoteToken
if(RemoteToken!=null){
用RemoteToken向IDM查询用户是否存在
if(用户存在){
//在SpringSecurity上下文中设置一个 RememberMeAuthenticationToken
//之后走SpringSecurity
SecurityContextHolder.getContext().setAuthentication(RememberMeAuthenticationToken);
}
}
}else{
跳转到IDM 的登录页面引导用户登录
}
}首先,我不希望去检查啥flowable cookie, 我甚至都不需要cookie, 因此,这个filter需要通过覆盖去掉
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public FlowableCookieFilterRegistrationBean flowableCookieFilterRegistrationBean(RemoteIdmService remoteIdmService,
FlowableCommonAppProperties properties) {
return new FlowableCookieFilterRegistrationBean(remoteIdmService, properties) {
@Override
protected void initializeFilter() {
if (getFilter() == null) {
FlowableCookieFilter flowableCookieFilter = new FlowableCookieFilter(remoteIdmService, properties) {
@Override
protected boolean skipAuthenticationCheck(HttpServletRequest request) {
return true;
}
};
setFilter(flowableCookieFilter);
}
}
};
}Flowable-UI-Modeler同样开启了SpringSecurity。在配置类
org.flowable.ui.modeler.conf.SecurityConfiguration.configureGlobal(AuthenticationManagerBuilder)
中注册了
org.flowable.ui.modeler.security.RemoteIdmAuthenticationProvider
其中又使用了
org.flowable.ui.common.service.idm.RemoteIdmService
通过Rest的方式调用Flowable IDM接口,最终认证的用户结果是一个
org.flowable.ui.common.security.FlowableAppUser
对象
这里为了使用我们自己的SpringBoot项目提供的登录认证接口,需要自己写一个AuthenticationProvider来替换Flowable的RemoteIdmAuthenticationProvider,实现调用远端认证接口,然后把结果组装成一个FlowableAppUser,让它接着往下走
package com.doublechain.flowable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.flowable.ui.common.model.RemoteUser;
import org.flowable.ui.common.security.DefaultPrivileges;
import org.flowable.ui.common.security.FlowableAppUser;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class CustomFlowableAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
@Getter
@Setter
@Value("${auth.url}")
//http://localhost:8880/flowable/secUserManager/login/username/password/
private String authUrl;
public CustomFlowableAuthenticationProvider() {
super();
}
@Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
String password = authentication.getCredentials().toString();
return getRemoteUser(username,password);
}
protected FlowableAppUser getRemoteUser(String login,String password) {
FormBody.Builder builder = new FormBody.Builder();
builder.add("username", login);
builder.add("password", password);
Request request = new Request.Builder().url(getAuthUrl()).post(builder.build()).build();
Response resp=null;
try {
resp = new OkHttpClient().newCall(request).execute();
String result = resp.body().string();
Map resultObject = new ObjectMapper().readValue(result, Map.class);
RemoteUser remoteUser = new RemoteUser();
remoteUser.setId((String) resultObject.get("id"));
remoteUser.setPassword((String) resultObject.get("pwd"));
remoteUser.setFirstName(remoteUser.getId());
List<SimpleGrantedAuthority> auths = new ArrayList<>();
auths.add(new SimpleGrantedAuthority(DefaultPrivileges.ACCESS_MODELER));
return new FlowableAppUser(remoteUser, remoteUser.getId(),auths );
} catch (IOException e) {
e.printStackTrace();
}finally {
Optional.ofNullable(resp).ifPresent(Response::close);
}
return null;
}
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
//TODO
}
}把自定义的auth provider注册到Spring Security中
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnProperty(prefix = "flowable.idm.ldap", name = "enabled", havingValue = "false", matchIfMissing = true)
public AuthenticationProvider authenticationProvider() {
return new CustomFlowableAuthenticationProvider();
}
@EnableWebSecurity
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomFormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().and().authenticationProvider(authenticationProvider()).authorizeRequests().anyRequest().authenticated().and().csrf().disable();
}
}最终,完整的Flowable-UI-Modeler启动类
package com.doublechain.flowable;
import javax.servlet.http.HttpServletRequest;
import org.flowable.ui.common.filter.FlowableCookieFilter;
import org.flowable.ui.common.filter.FlowableCookieFilterRegistrationBean;
import org.flowable.ui.common.properties.FlowableCommonAppProperties;
import org.flowable.ui.common.service.idm.RemoteIdmService;
import org.flowable.ui.modeler.conf.ApplicationConfiguration;
import org.flowable.ui.modeler.servlet.AppDispatcherServletConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationProvider;
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;
@Import({ ApplicationConfiguration.class, AppDispatcherServletConfiguration.class })
@SpringBootApplication
@EnableWebSecurity
public class FlowableApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(FlowableApplication.class, args);
}
/**
* 重写 Flowable 的 FlowableCookieFilterRegistrationBean,跳过cookie认证
*
* @param remoteIdmService
* @param properties
* @return
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public FlowableCookieFilterRegistrationBean flowableCookieFilterRegistrationBean(RemoteIdmService remoteIdmService, FlowableCommonAppProperties properties) {
return new FlowableCookieFilterRegistrationBean(remoteIdmService, properties) {
@Override
protected void initializeFilter() {
if (getFilter() == null) {
FlowableCookieFilter flowableCookieFilter = new FlowableCookieFilter(remoteIdmService, properties) {
@Override
protected boolean skipAuthenticationCheck(HttpServletRequest request) {
return true;
}
};
setFilter(flowableCookieFilter);
}
}
};
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnProperty(prefix = "flowable.idm.ldap", name = "enabled", havingValue = "false", matchIfMissing = true)
public AuthenticationProvider authenticationProvider() {
return new CustomFlowableAuthenticationProvider();
}
@EnableWebSecurity
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomFormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().and().authenticationProvider(authenticationProvider()).authorizeRequests().anyRequest().authenticated().and().csrf().disable();
}
}
}
5. 启动并验证
gradle bootRun, 把SpringBoot项目和改造的Flowable-UI-Modeler都启起来,
访问Flowable-UI-Modeler, 发现被带到了SpringSecurity默认登录页
输入用户名密码,就进去啦.
第三步:编辑流程,发布使用
如何在Flowable-UI-Modeler中编辑一个流程,这个还是以官方文档为参考最好。而且Flowable 编辑器设计总体来说也比较简明易懂,汉化不明白的地方,可以直接看看英文。
从Flowable-UI-Modeler发布流程定义到SpringBoot应用其实十分简单。
在SpringBoot应用中,我们已经通过引入
org.flowable.app.rest.service.api.repository.AppDeploymentCollectionResource获得了发布接口。接下来只需在Flowable-UI-Modeler的配置文件中配置接口地址就可以了
例如
flowable.modeler.app.deployment-api-url=http://localhost:8880/flowable
唯一需要注意一点的是,这里只是配一个前缀而不是全url
可以从源码
org.flowable.ui.modeler.service.AppDefinitionPublishService.deployZipArtifact(String, byte[], String, String)
中看到,Flowable-UI-Modeler在这个后面自己拼接了URI....
总结
我们创建了一个SpringBoot应用,一个Flowable-UI-Modeler应用
在SpringBoot应用中只引入Flowable SpringBoot starter以使用Flowable工作流引擎,引入了一个Flowable Rest中包含的Rest Resource为Flowable-UI-Moldeler提供流程定义发布接口
在Flowable-UI-Modeler中,我们重写了Spring Security部分,让Flowable-UI-Modeler使用自定义都用户身份和权限验证
我们在Flowable-UI-Modeler中编辑了流程定义,发布到了SpringBoot应用。
百度一搜Flowable Modeler集成,大量的文章都是拿着Flowable源码直接改。
建议大家还是尽量override.
Troubleshooting & QA
1 日志组件冲突:
logging SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/Users/jarryzhou/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.12.1/14973e22497adaf0196d481fb99c5dc2a0b58d41/log4j-slf4j-impl-2.12.1.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/Users/jarryzhou/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.2.3/7c4f3c474fb2c041d8028740440937705ebb473a/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
由于SpringBoot和Flowable一个用logback一个用log4j, 通过Starter引入他们的时候,这里会产生冲突,有2种解决方法,
一种是在其中某一个中忽略日志依赖,比如
compile 'org.flowable:flowable-spring-boot-starter:6.4.2'compile ('org.flowable:flowable-ui-modeler-app:6.4.2'){ exclude group: 'org.slf4j'}另一种是,使用implementation替代compile, 这样就各用各的。。
compile("org.springframework.boot:spring-boot-starter-web")
implementation 'org.flowable:flowable-ui-modeler-app:6.4.2'。。。。
2 启动时不自动建表
有可能是Liquibase禁用了,看配置文件是不是有
spring.liquibase.enabled=false
得改成true.
也有可能像我一样,mysql连接字符串加上nullCatalogMeansCurrent=true就好啦,
关于nullCatalogMeansCurrent,就请自行百度了。
3 插入中文数据报错
java.sql.SQLException: Incorrect string value: '\xE6\xB5\x8B\xE8\xAF\x95' for column 'name' at row 1at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) ~[mysql-connector-java-8.0.19.jar:8.0.19]at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) ~[mysql-connector-java-8.0.19.jar:8.0.19]at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-java-8.0.19.jar:8.0.19]at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953) ~[mysql-connector-java-8.0.19.jar:8.0.19]at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:370) ~[mysql-connector-java-8.0.19.jar:8.0.19]at com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44) ~[HikariCP-3.4.2.jar:na]建表的字符集不对,可以改flowable表的默认字符集,以mysql 为例子:
SET FOREIGN_KEY_CHECKS=0;
alter table ACT_APP_APPDEF convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_APP_DATABASECHANGELOG convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_APP_DATABASECHANGELOGLOCK convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_APP_DEPLOYMENT convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_APP_DEPLOYMENT_RESOURCE convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CMMN_CASEDEF convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CMMN_DATABASECHANGELOG convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CMMN_DATABASECHANGELOGLOCK convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CMMN_DEPLOYMENT convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CMMN_DEPLOYMENT_RESOURCE convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CMMN_HI_CASE_INST convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CMMN_HI_MIL_INST convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CMMN_HI_PLAN_ITEM_INST convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CMMN_RU_CASE_INST convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CMMN_RU_MIL_INST convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CMMN_RU_PLAN_ITEM_INST convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CMMN_RU_SENTRY_PART_INST convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CO_CONTENT_ITEM convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CO_DATABASECHANGELOG convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_CO_DATABASECHANGELOGLOCK convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_DE_DATABASECHANGELOGLOCK convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_DE_MODEL convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_DE_MODEL_HISTORY convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_DE_MODEL_RELATION convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_DMN_DATABASECHANGELOG convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_DMN_DATABASECHANGELOGLOCK convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_DMN_DECISION_TABLE convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_DMN_DEPLOYMENT convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_DMN_DEPLOYMENT_RESOURCE convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_DMN_HI_DECISION_EXECUTION convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_EVT_LOG convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_FO_DATABASECHANGELOG convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_FO_DATABASECHANGELOGLOCK convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_FO_FORM_DEFINITION convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_FO_FORM_DEPLOYMENT convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_FO_FORM_INSTANCE convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_FO_FORM_RESOURCE convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_GE_BYTEARRAY convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_GE_PROPERTY convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_HI_ACTINST convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_HI_ATTACHMENT convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_HI_COMMENT convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_HI_DETAIL convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_HI_ENTITYLINK convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_HI_IDENTITYLINK convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_HI_PROCINST convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_HI_TASKINST convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_HI_TSK_LOG convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_HI_VARINST convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_ID_BYTEARRAY convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_ID_GROUP convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_ID_INFO convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_ID_MEMBERSHIP convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_ID_PRIV convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_ID_PRIV_MAPPING convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_ID_PROPERTY convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_ID_TOKEN convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_ID_USER convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_PROCDEF_INFO convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RE_DEPLOYMENT convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RE_MODEL convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RE_PROCDEF convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RU_ACTINST convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RU_DEADLETTER_JOB convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RU_ENTITYLINK convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RU_EVENT_SUBSCR convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RU_EXECUTION convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RU_HISTORY_JOB convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RU_IDENTITYLINK convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RU_JOB convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RU_SUSPENDED_JOB convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RU_TASK convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RU_TIMER_JOB convert to character set utf8mb4 collate utf8mb4_bin;
alter table ACT_RU_VARIABLE convert to character set utf8mb4 collate utf8mb4_bin;