flowable介绍
flowable 是一个业务流程管理(BPM)和工作流系统,适用于开发人员和系统管理员。
它是著名 Java 工作流引擎 Activiti 的原作者从 Activiti 分支创建的新工作流引擎。其核心是超快速、稳定的 BPMN2 流程引擎。易于与 Spring 集成使用。
1、安装flowable
第一步:先安装docker sudo wget -qO- https://get.docker.com | sh 第二步:启动docker systemctl start docker 第三步: 安装flowable docker镜像 docker run -p8080:8080 flowable/flowable-rest 安装后启动报错, ctrl-c 退出后 安装如下: docker run -p8080:8080 flowable/all-in-one 第四步访问系统 admin 密码 test Flowable Modeler; http://localhost:8080/flowable-modeler Flowable Task; http://localhost:8080/flowable-task Flowable Admin; http://localhost:8080/flowable-admin Flowable IDM; http://localhost:8080/flowable-idm (login/password: admin/test)
2、springboot集成
参考:https://blog.csdn.net/liuwenjun05101/article/details/86669057
把里边代码下载下来:https://gitee.com/lwj/flow-modeler-sduty.git
3、项目集成
二种方式集成: 1)使用bpmn格式的.xml文件定义流程序 2)使用ui页面定义流程,数据会保存到数据库中(在act_de_model表)
第一种方式网上很多,在这里采用第二张方式:
1)在ui页面上定义流程
重启服务流程简单实例:
2)Check类、Restart类
public class Check implements JavaDelegate { @Override public void execute(DelegateExecution delegateExecution) { System.out.println("开始"); } }
public class Restart implements JavaDelegate { @Override public void execute(DelegateExecution delegateExecution) { Object count = delegateExecution.getVariable("count"); Integer count1 = (Integer) count; if(count1 == null) { count1 = 0; } // TODO 执行重启操作 如果重启失败:count+1 delegateExecution.setVariable("count",count1+1); System.out.println("执行"+count1+"次"); } }
3)部署运行
package org.flow.web.ui.rest; import org.flowable.bpmn.converter.BpmnXMLConverter; import org.flowable.bpmn.model.BpmnModel; import org.flowable.engine.ProcessEngine; import org.flowable.engine.RepositoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.spring.SpringProcessEngineConfiguration; import org.flowable.ui.modeler.domain.Model; import org.flowable.ui.modeler.serviceapi.ModelService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; /** * @Description: web方式测试访问:目的开启流程 * @Author: * @Version: 1.0 * @Create Date Time: 2019/9/3 15:29 * @Update Date Time: * @see */ @RestController @RequestMapping("/deployment") public class DeployMentController { @Autowired ModelService modelService; @Autowired TaskService taskService; @Autowired RepositoryService repositoryService; @Autowired SpringProcessEngineConfiguration springProcessEngineConfiguration; @GetMapping("/{id}") public String deployment(@PathVariable String id){ // 模型id,act_de_model表的id Model modelData = modelService.getModel(id); byte[] bytes = modelService.getBpmnXML(modelData); if(bytes==null){ return "请先设计流程并保存"; } BpmnModel model = modelService.getBpmnModel(modelData); if(model.getProcesses().size()==0){ return "数据模型不符要求,请至少设计一条主线流程。"; } byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model); String processName = modelData.getName()+".bpmn20.xml"; // 部署,部署之后的流程图才可以用来创建流程实例 // todo:部署之后的效果应该是一次部署多次使用,需要查看一下如何部署 repositoryService.createDeployment() .name(modelData.getName()) .addBytes(processName,bpmnBytes) .deploy(); return "部署成功"; } @GetMapping("/run/{key}") public String run(@PathVariable String key){ // 获取流程定义 // 获取流程引擎 ProcessEngine processEngine = springProcessEngineConfiguration.buildProcessEngine(); // 获取运行时service,可以在这里配置参数 RuntimeService runtimeService = processEngine.getRuntimeService(); // 添加全局事件监听,需实现FlowableEventListener接口 // runtimeService.addEventListener(); Map<String, Object> variables = new HashMap<String, Object>(); variables.put("count",1); // 创建流程实例并且指定参数,该参数在全局事件监听中是可以使用的。 runtimeService.startProcessInstanceByKey(id,variables); return "运行成功"; } }
4)访问:
http://localhost:8989/flow-study/deployment/id
id:该流程id
http://localhost:8989/flow-study/deployment/run/key
key:该流程的key值
flowable介绍
flowable 是一个业务流程管理(BPM)和工作流系统,适用于开发人员和系统管理员。
它是著名 Java 工作流引擎 Activiti 的原作者从 Activiti 分支创建的新工作流引擎。其核心是超快速、稳定的 BPMN2 流程引擎。易于与 Spring 集成使用。
Flowable 快速入门教程:SpringBoot 集成 Flowable + Flowable Modeler 流程配置可视化(超详细)
版本
这里选择的版本为 6.4.1
中文版用户手册:Flowable BPMN 用户手册
如果需要集成 Flowable Modeler 的请下载源码
PS:不要选择 6.4.2 版本,这个版本有发版问题
加依赖
由于是 spring-boot 集成,因此直接选择
flowable-spring-boot-starter
,里面提供了齐全的REST API
<!-- Flowable spring-boot 版套餐 --> <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring-boot-starter</artifactId> <version>6.4.1</version> </dependency>
其他的也可以直接选择
flowable-engine
<!-- flowable-engine --> <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-engine</artifactId> <version>6.4.1</version> </dependency>
加配置
# flowable 配置 flowable: # 关闭异步,不关闭历史数据的插入就是异步的,会在同一个事物里面,无法回滚 # 开发可开启会提高些效率,上线需要关闭 async-executor-activate: false
内部日志
Flowable 使用
SLF4J
作为内部日志框架。在这个例子中,我们使用log4j
作为SLF4J
的实现。加依赖
<!-- Flowable 内部日志采用 SLF4J --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency>
resource
目录下新建文件log4j.properties
log4j.rootLogger=DEBUG, CA log4j.appender.CA=org.apache.log4j.ConsoleAppender log4j.appender.CA.layout=org.apache.log4j.PatternLayout log4j.appender.CA.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n
初始化 ProcessEngine
代码初始化
// 流程引擎配置 ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration() .setJdbcUrl(url) .setJdbcUsername(username) .setJdbcPassword(password) .setJdbcDriver(driverClassName) // 初始化基础表,不需要的可以改为 DB_SCHEMA_UPDATE_FALSE .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); // 初始化流程引擎对象 ProcessEngine processEngine = cfg.buildProcessEngine();
flowable.cfg.xml 初始化
代码部分
// 流程引擎配置 ProcessEngineConfiguration cfg = ProcessEngineConfiguration // 根据文件名获取配置文件 //.createProcessEngineConfigurationFromResource("activiti.cfg.xml"); // 获取默认配置文件,默认的就是 activiti.cfg.xml .createProcessEngineConfigurationFromResourceDefault() // 初始化基础表,不需要的可以改为 DB_SCHEMA_UPDATE_FALSE .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); // 初始化流程引擎对象 ProcessEngine processEngine = cfg.buildProcessEngine();
新建
flowable.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="processEngineConfiguration" class="org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration"> <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test"/> <property name="jdbcDriver" value="com.mysql.jdbc.Driver"/> <property name="jdbcUsername" value="root"/> <property name="jdbcPassword" value="123456"/> <property name="databaseSchemaUpdate" value="true"/> </bean> </beans>
我的初始化示例
我的配置文件
ProcessEngineConfig.java
依赖
spring-boot-configuration-processor
加载配置文件lomok
简化 java 代码<!-- 配置文件处理器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
/**
* 流程引擎配置文件
* @author: linjinp
* @create: 2019-10-21 16:49
**/
@Configuration
@ConfigurationProperties(prefix = "spring.datasource")
@Data
public class ProcessEngineConfig {
private Logger logger = LoggerFactory.getLogger(ProcessEngineConfig.class);
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.publicKey}")
private String publicKey;
/**
* 初始化流程引擎
* @return
*/
@Primary
@Bean(name = "processEngine")
public ProcessEngine initProcessEngine() {
logger.info("=============================ProcessEngineBegin=============================");
// 流程引擎配置
ProcessEngineConfiguration cfg = null;
try {
cfg = new StandaloneProcessEngineConfiguration()
.setJdbcUrl(url)
.setJdbcUsername(username)
.setJdbcPassword(ConfigTools.decrypt(publicKey, password))
.setJdbcDriver(driverClassName)
// 初始化基础表,不需要的可以改为 DB_SCHEMA_UPDATE_FALSE
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE)
// 默认邮箱配置
// 发邮件的主机地址,先用 QQ 邮箱
.setMailServerHost("smtp.qq.com")
// POP3/SMTP服务的授权码
.setMailServerPassword("xxxxxxx")
// 默认发件人
.setMailServerDefaultFrom("836369078@qq.com")
// 设置发件人用户名
.setMailServerUsername("管理员")
// 解决流程图乱码
.setActivityFontName("宋体")
.setLabelFontName("宋体")
.setAnnotationFontName("宋体");
} catch (Exception e) {
e.printStackTrace();
}
// 初始化流程引擎对象
ProcessEngine processEngine = cfg.buildProcessEngine();
logger.info("=============================ProcessEngineEnd=============================");
return processEngine;
}
}
PS:这里没有单独对流程引擎中的 8 个核心服务做初始化,是因为使用 flowable-spring-boot-starter
依赖,会自动帮忙注册好,不需要自己再注册,直接使用即可
如果你使用的依赖是 flowable-engine
,你可能还需要
//八大接口
// 业务流程的定义相关服务
@Bean
public RepositoryService repositoryService(ProcessEngine processEngine){
return processEngine.getRepositoryService();
}
// 流程对象实例相关服务
@Bean
public RuntimeService runtimeService(ProcessEngine processEngine){
return processEngine.getRuntimeService();
}
// 流程任务节点相关服务
@Bean
public TaskService taskService(ProcessEngine processEngine){
return processEngine.getTaskService();
}
// 流程历史信息相关服务
@Bean
public HistoryService historyService(ProcessEngine processEngine){
return processEngine.getHistoryService();
}
// 表单引擎相关服务
@Bean
public FormService formService(ProcessEngine processEngine){
return processEngine.getFormService();
}
// 用户以及组管理相关服务
@Bean
public IdentityService identityService(ProcessEngine processEngine){
return processEngine.getIdentityService();
}
// 管理和维护相关服务
@Bean
public ManagementService managementService(ProcessEngine processEngine){
return processEngine.getManagementService();
}
// 动态流程服务
@Bean
public DynamicBpmnService dynamicBpmnService(ProcessEngine processEngine){
return processEngine.getDynamicBpmnService();
}
//八大接口 end
版本为 6.4.1
,不多说了,看文章开头下载源码
打开文件夹 flowable-ui-modeler
路径:flowable-engine-flowable-6.4.1\modules\flowable-ui-modeler
resource/static
下这些都是需要用到的
使用 rest,logic,conf 的依赖
<!-- flowable 集成依赖 rest,logic,conf -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-modeler-rest</artifactId>
<version>6.4.1</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-modeler-logic</artifactId>
<version>6.4.1</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-modeler-conf</artifactId>
<version>6.4.1</version>
</dependency>
在项目中的 resource
文件夹下新建一个 static
文件夹
SpringBoot 能自动读取 static
目录下的静态文件,因此文件夹名称不可随意更改
复制 flowable-ui-modeler-app
包中 resources\static
下所有文件,复制到新建的 static
下
路径:flowable-engine-flowable-6.4.1\modules\flowable-ui-modeler\flowable-ui-modeler-app\src\main\resources\static
复制以下文件到自己的项目中
ApplicationConfiguration.java
路径:flowable-engine-flowable-6.4.1\modules\flowable-ui-modeler\flowable-ui-modeler-conf\src\main\java\org\flowable\ui\modeler\conf
原因:这个文件是启动中必要的配置文件,需要做修改,详细的可以看下 app 中启动类,文件路径随意
AppDispatcherServletConfiguration.java
路径:flowable-engine-flowable-6.4.1\modules\flowable-ui-modeler\flowable-ui-modeler-conf\src\main\java\org\flowable\ui\modeler\servlet
原因:这个文件是启动中必要的配置文件,需要做修改,详细的可以看下 app 中启动类,文件路径随意
StencilSetResource.java
路径:flowable-engine-flowable-6.4.1\modules\flowable-ui-modeler\flowable-ui-modeler-rest\src\main\java\org\flowable\ui\modeler\rest\app
同时在 resource 下新建一个 stencilset 文件夹用来放汉化文件,可以直接下载我上传的
原因:国际化配置加载,为了使用我们自己的汉化文件因此把文件拿出来并修改,文件路径随意
PS:复制出来后要对这个文件进行重命名,否则会与 Jar 包里的文件产生 Bean 存在的冲突
我这重命名后叫 FlowableStencilSetResource.java
SecurityUtils
路径:flowable-engine-flowable-6.4.1\modules\flowable-ui-common\src\main\java\org\flowable\ui\common\security
原因:流程模型加载需要调用的工具类,文件路径需要与原路径保持一致
也就是包路径必须是 org.flowable.ui.common.security
这样在 Jar 中的方法在调用时会覆盖原 Jar 里的工具类
此文件不需要过多说明,主要移除 IDM 方面的配置
注意 conf 目录不要引入,里面也包含和 IDM 相关的配置
@Configuration
@EnableConfigurationProperties(FlowableModelerAppProperties.class)
@ComponentScan(basePackages = {
// "org.flowable.ui.modeler.conf", // 不引入 conf
"org.flowable.ui.modeler.repository",
"org.flowable.ui.modeler.service",
// "org.flowable.ui.modeler.security", //授权方面的都不需要
// "org.flowable.ui.common.conf", // flowable 开发环境内置的数据库连接
// "org.flowable.ui.common.filter", // IDM 方面的过滤器
"org.flowable.ui.common.service",
"org.flowable.ui.common.repository",
//
// "org.flowable.ui.common.security",//授权方面的都不需要
"org.flowable.ui.common.tenant" },excludeFilters = {
// 移除 RemoteIdmService
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = RemoteIdmService.class)
}
)
public class ApplicationConfiguration {
@Bean
public ServletRegistrationBean modelerApiServlet(ApplicationContext applicationContext) {
AnnotationConfigWebApplicationContext dispatcherServletConfiguration = new AnnotationConfigWebApplicationContext();
dispatcherServletConfiguration.setParent(applicationContext);
dispatcherServletConfiguration.register(ApiDispatcherServletConfiguration.class);
DispatcherServlet servlet = new DispatcherServlet(dispatcherServletConfiguration);
ServletRegistrationBean registrationBean = new ServletRegistrationBean(servlet, "/api/*");
registrationBean.setName("Flowable Modeler App API Servlet");
registrationBean.setLoadOnStartup(1);
registrationBean.setAsyncSupported(true);
return registrationBean;
}
}
同理,为了不引入 IDM 的配置
@Configuration
@ComponentScan(value = { "org.flowable.ui.modeler.rest.app",
// 不加载 rest,因为 getAccount 接口需要我们自己实现
// "org.flowable.ui.common.rest"
},excludeFilters = {
// 移除 EditorUsersResource 与 EditorGroupsResource,因为不使用 IDM 部分
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = EditorUsersResource.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = EditorGroupsResource.class),
// 配置文件用自己的
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = StencilSetResource.class),
}
)
@EnableAsync
public class AppDispatcherServletConfiguration implements WebMvcRegistrations {
private static final Logger LOGGER = LoggerFactory.getLogger(AppDispatcherServletConfiguration.class);
@Bean
public SessionLocaleResolver localeResolver() {
return new SessionLocaleResolver();
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LOGGER.debug("Configuring localeChangeInterceptor");
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
return localeChangeInterceptor;
}
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
LOGGER.debug("Creating requestMappingHandlerMapping");
RequestMappingHandlerMapping requestMappingHandlerMapping = new RequestMappingHandlerMapping();
requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
requestMappingHandlerMapping.setRemoveSemicolonContent(false);
Object[] interceptors = { localeChangeInterceptor() };
requestMappingHandlerMapping.setInterceptors(interceptors);
return requestMappingHandlerMapping;
}
}
这个主要保存时候会调这里的接口
将 getCurrentUserObject
方法进行修改,让他获取默认的 admin
/**
* @return the {@link User} object associated with the current logged in user.
*/
public static User getCurrentUserObject() {
if (assumeUser != null) {
return assumeUser;
}
RemoteUser user = new RemoteUser();
user.setId("admin");
user.setDisplayName("Administrator");
user.setFirstName("Administrator");
user.setLastName("Administrator");
user.setEmail("admin@flowable.com");
user.setPassword("123456");
List<String> pris = new ArrayList<>();
pris.add(DefaultPrivileges.ACCESS_MODELER);
pris.add(DefaultPrivileges.ACCESS_IDM);
pris.add(DefaultPrivileges.ACCESS_ADMIN);
pris.add(DefaultPrivileges.ACCESS_TASK);
pris.add(DefaultPrivileges.ACCESS_REST_API);
user.setPrivileges(pris);
return user;
}
新建文件 FlowableController
,自己随意
在加载页面时候会调用这个接口获取用户信息,由于我们绕过了登陆,因此给个默认的用户 admin
为了不和原文件冲突,所以 @RequestMapping("/login")
/**
* Flowable 相关接口
* @author linjinp
* @date 2019/10/31 10:55
*/
@RestController
@RequestMapping("/login")
public class FlowableController {
/**
* 获取默认的管理员信息
* @return
*/
@RequestMapping(value = "/rest/account", method = RequestMethod.GET, produces = "application/json")
public UserRepresentation getAccount() {
UserRepresentation userRepresentation = new UserRepresentation();
userRepresentation.setId("admin");
userRepresentation.setEmail("admin@flowable.org");
userRepresentation.setFullName("Administrator");
// userRepresentation.setLastName("Administrator");
userRepresentation.setFirstName("Administrator");
List<String> privileges = new ArrayList<>();
privileges.add(DefaultPrivileges.ACCESS_MODELER);
privileges.add(DefaultPrivileges.ACCESS_IDM);
privileges.add(DefaultPrivileges.ACCESS_ADMIN);
privileges.add(DefaultPrivileges.ACCESS_TASK);
privileges.add(DefaultPrivileges.ACCESS_REST_API);
userRepresentation.setPrivileges(privileges);
return userRepresentation;
}
}
路径:resource\static\scripts\configuration\url-conf.js
将 getAccountUrl 的路径改为上面自己的 getAccount 接口的路径
记得重命名,我这重命名后叫 FlowableStencilSetResource
把配置文件路径改为我们自己目录下的路径
stencilset/stencilset_bpmn.json
与 stencilset/stencilset_cmmn.json
主要修改三个
自己目录
下的 ApplicationConfiguration 与 AppDispatcherServletConfiguration,可参考 app 的启动器Jar 包
里的 DatabaseConfiguration,这个文件是对表进行更新的,由于 conf
目录不引入,因此我们只能单独引入,具体内容可以自己看下这个文件@SpringBootApplication(exclude={SecurityAutoConfiguration.class})
@SpringBootApplication(exclude={SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, SecurityFilterAutoConfiguration.class})
//启用全局异常拦截器
@Import(value={
// 引入修改的配置
ApplicationConfiguration.class,
AppDispatcherServletConfiguration.class,
// 引入 DatabaseConfiguration 表更新转换
DatabaseConfiguration.class})
// Eureka 客户端
@EnableDiscoveryClient
@ComponentScan(basePackages = {"com.springcloud.*"})
@MapperScan("com.springcloud.*.dao")
// 移除 Security 自动配置
// Spring Cloud 为 Finchley 版本
// @SpringBootApplication(exclude={SecurityAutoConfiguration.class})
// Spring Cloud 为 Greenwich 版本
@SpringBootApplication(exclude={SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, SecurityFilterAutoConfiguration.class})
public class FlowableApplication {
public static void main(String[] args) {
SpringApplication.run(FlowableApplication.class, args);
}
}
https://localhost:8087/
自动跳转
创建完数据库后,关闭自动更新。原因是更新的标准并非是你引入的流程引擎的版本,而是官方发布的版本,所以如果一直开启,以后重启之类的可能导致提示版本升级失败,毕竟你的依赖版本并没有升级。
Factory method 'initProcessEngine' threw exception; nested exception is
org.flowable.common.engine.api.FlowableException:
Could not update Flowable database schema: unknown version from database: '6.5.0.1'
因此除非你确实要提高你的引擎版本到最新,否则不要开启
假如你出现了上述问题,可尝试:
1.删掉所有表重建
这样会创建你当前版本的数据库,这种肯定可以,但是基本上数据是没了,除非你有耐心迁移下。
2.直接修改当前数据库版本
就是这张 ACT_GE_PROPERTY
的数据,如果出问题了,这里的版本就会变成更新的版本,如:6.5.0.1,状态从创建变为更新,手动直接修正所有参数。本人没尝试过这种方式,应该可行。
首页不建议将业务代码和流程引擎混在一个项目中
如果一定要这样,遇到自己的 XML 总扫描不到,转下面的文章
SpringBoot 集成 Flowable + Flowable Modeler 导致自身 XML 扫描不到解决方案
文章如果存在什么问题,请及时留言反馈
集成后的代码:https://gitee.com/linjinp-spring-cloud/linjinp-spring-cloud
代码在 flowable-demo
包,IDEA Active profiles
配置为 sit
测试分支,单独启动即可
目前比较出名的开源工作流框架大概有4个,分别是Activiti/camunda/Flowable/Jbpmn。下面我们先抛开Jbpm框架,重点对比下Activiti/camunda/Flowable三个框架,因为这三个框架同宗同源,几乎都是从Jbpm4之后衍生出来的。
随着国内越来越多的企业大量的开始使用工作流框架,工作流框架的选型也成为一个首要并且头痛的事情。因为开源的工作流框架实在是太多了,用多如牛毛形如也不为过。从而使好多企业感觉无从下手去选型。因为从表面(API)层面看都差不多,但是深入内核去研究又没有太多的时间和精力。在本文中我们重点从这几个框架(Activiti/camunda/Flowable)的发展史。后续博文会介绍几个技术的选型。
Activiti/camunda/Flowable商业化情况
2012年末,Alfresco的Activiti BPM 小组正经历一系列的转变:Tom Baeyens——Activiti最初的创建者和开发者(宣布担任Effektif云BPM启动的现任CEO)——将不再领导Activiti工 程,并已决定在任职不到三年后离开Alfresco,。camunda是Activiti最大的贡献者之一(除Alfresco以外),同时也是它一个主 要的执行咨询合作伙伴。camunda表示Activiti可能太拘束于Alfresco对以文档为中心的工作流的需求(这个也是BPMN约束使然),而忽视了Activiti起步时 的更为普遍的BPM平台。camunda宣布他们正从Activiti 分裂出一个新的开源工程,那就是camunda BPM.
这在开源BPM界无疑是重磅新闻。BPM界早已存在几大巨头,比如Activiti, BonitaSoft, jBPM和Processmaker, 我们并不清楚是否还有足够的开源BPM软件需求以保证新的加入者的加盟。同时,两大阵营必然会有些心生不快。在这个小社会中,树敌的代价将是你无法承受 的,因为你永远不会知道在未来几年中你终将要和谁合作。这样的离别被形容为“悲伤”,不管是在camunda他们的公告中还是在Joram Barrez (Activiti5/6版本的核心开发者,目前已经加入Flowable阵营)的邮件中;也把Activiti和camunda置于直接敌对的位置,在现有Activiti用户和未来业务上进行 竞争。 Signavio——其流程建模人员深度整合camunda BPM——下发了一个新闻稿声明camunda BPM的分裂将对Signavio的客户有利,并且Tom Baeyens给出了不错的报价;记住Signavio刚为Baeyens的新启动提供了资金。这就像是 BPM的《冷暖人间》。
camunda BPM,正如Activiti(jBPM,就此而言)并未声称要做零代码BPM组件——有些人可能会辩称即使他们声称是,他们也不是——但BPM引擎和功 能的目的是嵌入业务线的企业应用程序中。他们看到零代码市场在非战略性流程中是通用工具,而且很有可能通过外包或云解决方案(Effektif,任何一 个?)能同样好甚至更好地提供服务;反之,camunda将目标定位于:IT占据竞争优势,而BPM仅仅是一个更大的应用中的一部分功能。这并不意味着这 里对非技术性业务分析师毫无意义:BPMN作为连接业务与IT的桥梁,camunda则把他们之前专有的BPMN往返能力带到新的开源工程中去。他们在 Eclipse下的BPMN插件中为业务分析师提供了一种易于使用的建模器,或与Signavio,Adonis和其他建模工具实现往返。早在2012年 6月,camunda博客就讨论过如何使用camunda BPM整合几个不同的BPMN建模器,尽管他们明显更青睐Signavio。
camunda BPM是一个在Apache 许可下完全开源的BPM堆栈(Eclipse设计者、开发者UI的框架是使用Eclipse公共许可)。社区(开源)版通常是最新的版本,注意:一些商业 开源供应商把他们的社区版本归入到商业版中以提高收益,而企业(商业)版在接收进一步的测试和集成上总是稍慢一步。企业版中唯一专有的可用功能就是应用服 务器(WAS)集成和Cockpit Pro,一个监控管理工具,虽然社区版中有一个Cockpit Light功能。你可以在这里看到一个社区-企业特征的对比和一个更为完整的清单。除非你从一开始就被WAS束缚了或者需要相当多的支持,社区版是可能足 以让你开始初期运行的,这样从开源到商业版的过渡更为容易。
然而,问题并非真的是camunda是否将对Activiti代码基做出巨大贡献(他们确实做了),而是他们是否能维持并建立一个Activiti 的开源叉形指令。他们内部有一些好的人选提供——负责核心流程引擎架构师的Daniel Meyer,技术咨询/产品管理视图的Bernd Rücker, BPM业务方面的Jakob Freund和一个具有Activiti和camunda代码基经验的开发团队。他们在Activiti开源社区和开发中展现出卓越的领导力,所以很可能 有能力运作一个camunda BPM开源社区,但是需要确保他们用足够的资源来保持它的重要地位。在德国,早已有一个camunda社区,但那并不同于开源社区,而且那只是在德国,所 以他们还有许多工作要做。
还有就是现有的Activiti和camunda用户。现有的camunda客户可能并不会被这次的分裂吓到,因为不管怎样对他们来说重要的贡献都 是camunda做出的,但现有的Activiti用户(和潜在顾客)可不会轻易妥协于camunda。他们可能正在附加功能与Activiti背后所意 味着的更大的公司,稳定的品牌和现有社区之间权衡利弊。鉴于一些新的UI特性从Alfresco团队整合到Activiti,公正地说,Alfresco 将继续引领Activiti,并试图维持他们在开源BPM市场上屹立不倒。现有Activiti用户如果想转向camunda BPM可能会有一个小窗口:现在,引擎是相同的而迁移是微不足道的,但是我期待着在6个月之内,双方都将在他们各自的项目中做出足够的改变,而那将是一项 更重要的工作。换言之,如果你现在正使用Activiti或camunda且又考虑转换,那么现在就行动吧。
camunda可能会得罪一些人,因为他宣告开源分裂而不是仅仅把他们专有的产品整合到Activiti工程中;这么做他们可能能成为一个更强大的 影响者,杜绝任何(感观的)来自Alfresco的以文档为中心的影响。再次声明,我并非他们任何一家公司的内部员工,也不是Activiti开源社区的 一份子,所以这些都仅仅是推测。
上文也提到过,Activiti5在开发期间,衍生了一个Camunda框架,并且当时的Tom Baeyens已经离职,tijsrademakers开始担任领导并全面负责Activiti5的发展,Joram Barrez担任架构师的职位,两位强强联手,一群好基友引领activiti快速迭代的发展着,支持更多的BPMN规范要素以及修复这铺天盖地的bug。全世界的宣扬Activiti各种各样的好,风光一时。期间还出版了一本Activiti in Action一书,此书非国人咖啡兔写的Activiti实战一书。这两本书内容风格还是完全不一样,tijsrademakers写的Activiti in Action书的封面如下:
我在写Activiti权威指南的时候,大概是2016年7月份左右。给清华大学出版社交稿的时候大概在2017年3月份左右、最终出版是2017年5月份左右。
其实在2016.7~2017.5期间activiti团队内部已经产生了重大的分歧。名义上说是分歧,其实是Activiti这几年止步不前,开始吃老本。而这几年又出了CMMN/DMN两个新规范以及BPMN更多的规范,Camunda框架已经率先响应并支持了,而Activiti5还是不能支持这些规范的,导致流失很多用户,很多用户转向了Camunda阵营,activiti也开始着急忙慌的想去实现CMMN以及DMN规范,但是在实现DMN规范的过程中,还在重构代码,那就是在用户层面增加了更多的引擎,包括内容引擎、应用引擎等,在内核方面因为还是使用的PVM技术,导致在支持流程动态化方面有些吃力,bug层出不穷,前段框架选型失败,后端重构错误等原因。
前段选型失败最大的看点就是Activiti6的DMN设计器完全没法去正常使用,Flowable6的时候又重写了DMN设计器。
后端重构包括XML的解析、PVM的去除。XML部分代码重构之后,导致很多元素的解析被遗漏,bug层出不穷。目前Flowable6.4之前的版本还有不少元素没有被解析。PVM被移除看起来简单了,这也意味这没有PVM就不会再去支持其他的BPEL语言的引擎。这个对于大公司的战略布局来看,确实丧失了很多的优势,因此tijsrademakers一批开发团队已经离职,在activiti6框架基础之上去开发flowable框架了。因为不在兼容其他的的BPEL语言的引擎。后续还会说其他的工作流引擎。
关于新的activiti新团队与原有的团队重要开发人员我们罗列一下,细节如下:
上图是Tijs Rademakers,算是activiti5以及6比较核心的leader了。现在是flowable框架的leader。
Joram Barrez 算是activiti5以及6比较核心的leader了。目前从事flowable框架开发。
Salaboy Activiti Cloud BPM leader(Activiti Cloud BPM 也就是目前的activiti7框架)
Tijs Rademakers以及Salaboy目前是两个框架的leader。
特此强调一点:activiti5以及activiti6、flowable是Tijs Rademakers团队开发的。
Activiti7是 Salaboy团队开发的。activiti6以及activiti5代码目前有 Salaboy团队进行维护。因为Tijs Rademakers团队去开发flowable框架了,所以activiti6以及activiti5代码已经交接给了 Salaboy团队(可以理解为离职之前工作交接)。目前的activiti5以及activiti6代码还是原Tijs Rademakers原有团队开发的。Salaboy团队目前在开发activiti7框架。对于activiti6以及activiti5的代码官方已经宣称暂停维护了。activiti7就是噱头 内核使用的还是activiti5。并没有为引擎本身注入更多的新特性,只是在activiti之外的上层封装了一些应用。旨在简化流程的开发以及适应敏捷开发模式。
注意:activiti6的很多框架bug在flowable框架中已经修复的差不多了。activiti6就是flowable的rc1版本,flowable的rc1版本与flowable6.4.1版本差距是2年,bug之多可想而知,两个框架差距了2年,而且目前activiti6官方也停止维护了。
为了实现工作流自动化,许多组织购买了BPMS(也称为“BPM套件”),通常由较大的供应商提供。 在许多情况下,这些产品无法兑现承诺,更糟糕的是,他们对用户施加了以下一个或多个问题:
因此,许多组织决定摆脱这些遗留系统,并用Camunda BPM取代他们的BPMS。
Camunda从一开始就是为开发人员设计的:
Camunda也照顾业务用户,Camunda BPM为非开发人员提供了广泛的工具:
Camunda还包括决策模型和表示法(DMN)决策引擎,以便业务用户可以定义和维护直接与工作流引擎集成的可执行业务规则。
Camunda BPM是Apache 2.0许可下的开源软件,这意味着您可以直接访问源代码和最小的供应商锁定。
流程迁移是直截了当的,我们在这里提供帮助
从传统的BPMS迁移到Camunda BPM并非一蹴而就,但它仍然是一个简单的过程。
Camunda BPM多次取代以下产品:
关于更多的Activiti6视频地址:https://ke.qq.com/course/package/11402?tuin=84de321b
关于更多的Flowable视频地址:https://ke.qq.com/course/package/11431?tuin=84de321b
关于更多的camunda视频2019年3月份即将上市。