1 | DmnEngine dmnEngine = DMNEngines.getDefaultDmnEngine()
16 | <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="dmnEngineConfiguration" class="org.flowable.dmn.engine.impl.cfg.StandaloneDmnEngineConfiguration">
<property name="jdbcUrl" value="jdbc:h2:mem:flowable;DB_CLOSE_DELAY=1000" />
<property name="jdbcDriver" value="org.h2.Driver" />
<property name="jdbcUsername" value="sa" />
<property name="jdbcPassword" value="" />
<property name="databaseSchemaUpdate" value="true" />
</bean>
</beans>
请注意这个配置XML实际上是一个Spring配置文件。但这并不意味着Flowable DMN只能用于Spring环境!我们只是简单利用Spring内部的解析与依赖注入功能来构造引擎。
也可以通过编程方式使用配置文件,来构造DMNEngineConfiguration对象。也可以使用不同的bean id(例如第3行)。
6 | DmnEngineConfiguration.
createDmnEngineConfigurationFromResourceDefault();
createDmnEngineConfigurationFromResource(String resource);
createDmnEngineConfigurationFromResource(String resource, String beanName);
createDmnEngineConfigurationFromInputStream(InputStream inputStream);
createDmnEngineConfigurationFromInputStream(InputStream inputStream, String beanName);
4 | DmnEngine dmnEngine = DmnEngineConfiguration.createStandaloneInMemDmnEngineConfiguration()
.setDatabaseSchemaUpdate(DmnEngineConfiguration.DB_SCHEMA_UPDATE_FALSE)
.setJdbcUrl("jdbc:h2:mem:my-own-db;DB_CLOSE_DELAY=1000")
.buildDmnEngine();
org.flowable.dmn.engine.impl.cfg.StandaloneDmnEngineConfiguration: 流程引擎独立运行。Flowable自行处理事务。在默认情况下,数据库检查只在引擎启动时进行(如果Flowable DMN表结构不存在或表结构版本不对,会抛出异常)。
org.flowable.dmn.engine.impl.cfg.StandaloneInMemDmnEngineConfiguration: 这是一个便于使用单元测试的类。Flowable DMN自行处理事务。默认使用H2内存数据库。数据库会在引擎启动时创建,并在引擎关闭时删除。使用这个类时,很可能不需要更多的配置。
org.flowable.dmn.spring.SpringDmnEngineConfiguration: 在流程引擎处于Spring环境时使用。查看Spring集成章节获得更多信息。
2.3. 插入流程引擎 Plug into Process Engine
除了在独立模式下运行之外,也可能需要将DMN引擎插入流程引擎中。这样可以使流程引擎能够使用DMN和其他引擎。这样就可以,例如,让流程引擎的部署服务API可以部署的包中,不止包含BPMN模型,还可以包含DMN模型。
要在流程引擎中使用DMN引擎,需要在流程引擎配置文件的configurators列表中,添加org.flowable.dmn.engine.configurator.DmnEngineConfigurator。
20 | <bean id="dmnEngineConfiguration" class="org.flowable.dmn.engine.impl.cfg.StandaloneDmnEngineConfiguration">
<property name="jdbcUrl" value="jdbc:h2:mem:flowable;DB_CLOSE_DELAY=1000" />
<property name="jdbcDriver" value="org.h2.Driver" />
<property name="jdbcUsername" value="sa" />
<property name="jdbcPassword" value="" />
</bean>
<bean id="dmnEngineConfigurator" class="org.flowable.dmn.engine.configurator.DmnEngineConfigurator">
<property name="dmnEngineConfiguration" ref="dmnEngineConfiguration" />
</bean>
<bean id="processEngineConfiguration" class="org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="configurators">
<ref bean="dmnEngineConfigurator" />
</list>
</property>
</bean>
4 | <property name="jdbcUrl" value="jdbc:h2:mem:flowable_dmn;DB_CLOSE_DELAY=1000" />
<property name="jdbcDriver" value="org.h2.Driver" />
<property name="jdbcUsername" value="sa" />
<property name="jdbcPassword" value="" />
12 | <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" >
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/flowable_dmn" />
<property name="username" value="flowable" />
<property name="password" value="flowable" />
<property name="defaultAutoCommit" value="false" />
</bean>
<bean id="dmnEngineConfiguration" class="org.flowable.dmn.engine.impl.cfg.StandaloneDmnEngineConfiguration">
<property name="dataSource" ref="dataSource" />
databaseType: 通常不需要专门设置这个参数,因为它可以从数据库连接信息中自动检测得出。只有在自动检测失败时才需要设置。可用值:{h2, mysql, oracle, postgres, mssql, db2}。这个选项会决定创建、删除与查询时使用的脚本。查看“支持的数据库”章节了解我们支持哪些类型的数据库。
databaseSchemaUpdate: 用于设置流程引擎启动关闭时使用的数据库表结构控制策略。
false (默认): 当引擎启动时,检查数据库表结构的版本是否匹配库文件版本。版本不匹配时抛出异常。
true : 构建引擎时,检查并在需要时更新表结构。表结构不存在则会创建。
create-drop : 引擎创建时创建表结构,并在引擎关闭时删除表结构。
2.5. JNDI数据源配置 JNDI Datasource Configuration
默认情况下,Flowable DMN的数据库配置保存在每个web应用WEB-INF/classes目录下的db.properties文件中。有时这样并不合适,因为这需要用户修改Flowable源码中的db.properties文件并重新编译war包,或者在部署后解开war包并修改db.properties文件。
通过使用JNDI(Java Naming and Directory Interface,Java命名和目录接口)获取数据库连接时,连接就完全由Servlet容器管理,并可以在war部署之外管理配置。同时也提供了比db.properties中更多的控制连接的参数。
2.5.1. 配置 Configuration
根据你使用的servlet容器应用不同,配置JNDI数据源的方式也不同。下面的介绍用于Tomcat,对于其他容器应用,请参考对应的文档。
Tomcat的JNDI资源配置在$CATALINA_BASE/conf/[enginename]/[hostname]/[warname].xml (对于Flowable UI应用,通常会是$CATALINA_BASE/conf/Catalina/localhost/flowable-app.xml)。当应用第一次部署时,默认会从Flowable war包中复制context.xml。所以如果存在这个文件则需要替换。例如,如果需要将JNDI资源修改为应用连接MySQL而不是H2,按照下列修改文件:
16 | <?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/flowable-app">
<Resource auth="Container"
name="jdbc/flowableDB"
type="javax.sql.DataSource"
description="JDBC DataSource"
url="jdbc:mysql://localhost:3306/flowable"
driverClassName="com.mysql.jdbc.Driver"
username="sa"
password=""
defaultAutoCommit="false"
initialSize="5"
maxWait="5000"
maxActive="120"
maxIdle="5"/>
</Context>
| mysql |
jdbc:mysql://localhost:3306/flowable_dmn?autoReconnect=true |
已使用mysql-connector-java数据库驱动测试 |
oracle |
jdbc:oracle:thin:@localhost:1521:xe |
postgres |
jdbc:postgresql://localhost:5432/flowable_dmn |
jdbc:db2://localhost:50000/flowable_dmn |
mssql |
jdbc:sqlserver://localhost:1433;databaseName=flowable_dmn (jdbc.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver) 或 jdbc:jtds:sqlserver://localhost:1433/flowable_dmn (jdbc.driver=net.sourceforge.jtds.jdbc.Driver) |
已使用Microsoft JDBC Driver 4.0 (sqljdbc4.jar)与JTDS Driver测试 |
<beans >
<bean id="dmnEngineConfiguration" class="org.flowable.dmn.engine.impl.cfg.StandaloneDmnEngineConfiguration">
<!-- ... -->
<property name="databaseSchemaUpdate" value="true" />
<!-- ... -->
</bean>
</beans>
默认情况下,Flowable引擎依赖中不提供SFL4J绑定jar。你需要自行将其加入你的项目,以便使用所选的日志框架。如果没有加入实现jar,SLF4J会使用NOP-logger。这时除了一条警告外,不会记录任何日志。可以从http://www.slf4j.org/codes.html#StaticLoggerBinder了解关于绑定的更多信息。
可以像这样(这里使用Log4j)使用Maven添加依赖,请注意你还需要加上版本:
4 | <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
重要提示:当使用classpath中带有commons-logging的容器时:为了将spring的日志路由至SLF4j,需要使用桥接(参考http://www.slf4j.org/legacy.html#jclOverSLF4J)。如果你的容器提供了commons-logging实现,请按照http://www.slf4j.org/codes.html#release页面的指示来保证稳定性。
使用Maven的示例(省略了版本):
4 | <dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
4 | DmnEngine dmnEngine = DmnEngines.getDefaultDmnEngine();
DmnRuleService dmnRuleService = dmnEngine.getDmnRuleService();
DmnRepositoryService dmnRepositoryService = dmnEngine.getDmnRepositoryService();
DmnManagementService dmnManagementService = dmnEngine.getDmnManagementService();
DmnEngines.getDefaultDmnEngine() 在第一次被调用时将初始化并构建DMN引擎,在之后的调用都会返回相同的DMN引擎。DMN引擎的创建通过DMNEngines.init() 实现,关闭由DMNEngines.destroy() 实现。
DmnEngines会扫描所有flowable.dmn.cfg.xml 与flowable-dmn-context.xml 文件。对于所有的flowable.dmn.cfg.xml 文件,DMN引擎会以标准Flowable方式构建引擎:DmnEngineConfiguration.createDmnEngineConfigurationFromInputStream(inputStream).buildDmnEngine() 。对于所有的flowable-dmn-context.xml 文件,DMN引擎会以Spring的方式构建:首先构建Spring应用上下文,然后从该上下文中获取DMN引擎。
所有的服务都是无状态的。这意味着你可以很容易的在集群环境的多个节点上运行Flowable DMN,使用同一个数据库,而不用担心上一次调用实际在哪台机器上执行。不论在哪里执行,对任何服务的任何调用都是幂等(idempotent)的。
DmnRepositoryService很可能是使用Flowable DMN引擎要用的第一个服务。这个服务提供了管理与控制部署(deployments) 与DMN定义(DMN definitions) 的操作。DMN定义是DMN模型的基础概念(DMN的主要概念在DMN介绍章节中介绍)。它包含了选择(decision) 以及对应的选择表(decision table) 。部署(deployment) 是Flowable DMN引擎中的包装单元,一个部署中可以包含多个DMN XML文件。部署意味着将它上传至引擎,引擎将在储存至数据库之前检查与分析所有的DMN定义。从这里开始,可以在系统中使用这个部署,部署中包含的所有选择都可以使用。
此外,这个服务还可以:
* 按照key查找并执行一个选择。 --Execute a decision identified by it's key.
* @param decisionKey 选择的key,不能为null。 --the decision key, cannot be null
* @param inputVariables 包含输入变量的map。 --map with input variables
* @return 执行的{@link RuleEngineExecutionResult}。 --the {@link RuleEngineExecutionResult} for this execution
* @throws FlowableObjectNotFoundException
* 如果给定key不存在。 --when the decision with given key does not exist.
* @throws FlowableException
* 如果执行选择时发生错误。 --when an error occurs while executing the decision.
RuleEngineExecutionResult executeDecisionByKey(String decisionKey, Map<String, Object> inputVariables);
在上例中,如果传递的key找不到选择,会抛出异常。并且,由于javadoc中明确要求decisionKey不能为null,因此如果传递了null 值,会抛出FlowableIllegalArgumentException 异常。
尽管我们想避免过大的异常层次结构,在特定情况下仍然会抛出下述异常子类。所有流程执行与API调用中发生的错误,如果不符合下面列出的异常,会统一抛出FlowableExceptions 。
FlowableClassLoadingException : 当需要载入的类(如JavaDelegates, TaskListeners, …)无法找到,或载入时发生错误时抛出。
FlowableObjectNotFoundException : 当请求或要操作的对象不存在时抛出。
FlowableIllegalArgumentException : 这个异常说明调用Flowable DMN API时使用了不合法的参数。可能是引擎配置中的不合法值,或者是API调用传递的不合法参数。
4 | List<DmnDeployment> dmnDeployments = dmnRepositoryService.createDeploymentQuery()
.deploymentNameLike("deployment%")
.orderByDeployTime()
.list();
7 | long count = dmnRepositoryService.createNativeDeploymentQuery()
.sql("SELECT count(*) FROM " + dmnManagementService.getTableName(DmnDeploymentEntity.class) + " D1, "
+ dmnManagementService.getTableName(DecisionTableEntity.class) + " D2 "
+ "WHERE D1.ID_ = D2.DEPLOYMENT_ID_ "
+ "AND D1.ID_ = #{deploymentId}")
.parameter("deploymentId", deployment.getId())
.count();
Flowable支持JUnit V4的单元测试风格。撰写JUnit 4测试用例时,可以使用org.flowable.dmn.engine.test.FlowableDmnRule Rule。使用这个Rule,就可以通过getter访问DMN引擎和服务。引入这个Rule 就可以使用org.flowable.dmn.engine.test.DmnDeploymentAnnotation 注解(参见上例解释其用途及配置),并且会自动在classpath中寻找默认配置文件。当使用相同的配置资源时,流程引擎会静态缓存,以用于多个单元测试。也可以为Rule提供自定义的引擎配置。
下面的代码片段展示了JUnit 4风格的测试与FlowableDmnRule 的用法(并传入了一个可选的自定义配置):
20 | public class MyDecisionTableTest {
@Rule
public FlowableDmnRule flowableDmnRule = new FlowableDmnRule("custom1.flowable.dmn.cfg.xml");
@Test
@DmnDeploymentAnnotation
public void ruleUsageExample() {
DmnEngine dmnEngine = flowableDmnRule.getDmnEngine();
DmnRuleService dmnRuleService = dmnEngine.getDmnRuleService();
Map<String, Object> inputVariables = new HashMap<>();
inputVariables.put("inputVariable1", 2);
inputVariables.put("inputVariable2", "test2"
);
RuleEngineExecutionResult result = dmnRuleService.executeDecisionByKey("decision1", inputVariables);
Assert.assertEquals("result2", result.getResultVariables().get("outputVariable1"));
3.5. Web应用中的DMN引擎 The DMN engine in a web application
DmnEngine 是线程安全的类,可以很容易地在多个线程间共享。在web应用中,这意味着可以在容器启动时创建DMN引擎,并在容器关闭时关闭引擎。
下面的代码片段展示了如何在纯Servlet环境中,简单的通过ServletContextListener 初始化与销毁流程引擎。
11 | public class DmnEnginesServletContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
DmnEngines.init();
public void contextDestroyed(ServletContextEvent servletContextEvent) {
DmnEngines.destroy();
7 | <bean id="dmnEngineConfiguration" class="org.flowable.dmn.spring.SpringDmnEngineConfiguration">
</bean>
<bean id="dmnEngine" class="org.flowable.dmn.spring.DmnEngineFactoryBean">
<property name="dmnEngineConfiguration" ref="dmnEngineConfiguration" />
</bean>
4.2. 自动部署资源 Automatic resource deployment
集成Spring还提供了部署资源的特殊方式。在DMN引擎配置中,可以指定一组资源。当DMN引擎被创建时,这些资源都会被扫描并部署。有过滤器用于阻止重复部署。只有当资源确实发生变化时,才会重新部署至Flowable DMN数据库。在Spring容器经常重启(例如测试时)的时候,这很有用。
这里有个例子:
9 | <bean id="dmnEngineConfiguration" class="org.flowable.spring.SpringDmnEngineConfiguration">
<property name="deploymentResources"
value="classpath*:/org/flowable/spring/test/autodeployment/autodeploy/decision*.dmn" />
</bean>
<bean id="dmnEngine" class="org.flowable.dmn.spring.DmnEngineFactoryBean">
<property name="dmnEngineConfiguration" ref="dmnEngineConfiguration" />
</bean>
默认情况下,这个配置会将符合这个过滤器的所有资源组织在一起,作为Flowable DMN引擎的一个部署。重复检测过滤器将作用于整个部署,避免重复地部署未改变资源。有时这不是你想要的。例如,如果用这种方式部署了一组DMN资源,即使只有其中的一个资源发生了改变,整个部署都会被认为已经改变,因此这个部署中所有的所有DMN定义都会被重新部署。这将导致每个DMN定义都会刷新版本号,即使实际上只有一个DMN发生了变化。
可以使用SpringDmnEngineConfiguration 中的额外参数deploymentMode ,定制部署的选择方式。这个参数定义了在一组符合过滤器的资源中,组织部署的方式。默认这个参数有3个可用值:
single-resource : 为每个资源创建一个单独的部署,并用于重复检测过滤。当你希望单独部署每一个DMN定义,并且在它发生变化时创建新的DMN定义版本,应该使用这个值。
resource-parent-folder : 为同一个目录下的资源创建一个单独的部署,并用于重复检测过滤。这个参数值可以为大多数资源创建独立的部署。同时仍可以通过将部分资源放在同一个目录下,将它们组织在一起。这里有一个将deploymentMode 设置为single-resource 的例子:
6 | <bean id="dmnEngineConfiguration"
class="org.flowable.dmn.spring.SpringDmnEngineConfiguration">
<property name="deploymentResources" value="classpath*:/flowable/*.dmn" />
<property name="deploymentMode" value="single-resource" />
</bean>
25 | @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:org/flowable/spring/test/junit4/springTypicalUsageTest-context.xml")
public class SpringJunit4Test {
@Autowired
private DmnEngine dmnEngine;
@Autowired
private DmnRuleService ruleService;
@Autowired
@Rule
public FlowableDmnRule flowableSpringRule;
@Test
@DmnDeploymentAnnotation
public void simpleDecisionTest() {
Map<String, Object> inputVariables = new HashMap<>();
inputVariables.put("input1", "testString");
RuleEngineExecutionResult executionResult = ruleService.executeDecisionByKey("decision1", inputVariables);
assertEquals("test1", executionResult.getResultVariables().get("output1"));
3 | <bean id="flowableDmnRule" class="org.flowable.dmn.engine.test.FlowableDmnRule">
<property name="dmnEngine" ref="dmnEngine"/>
</bean>
7 | String dmnDefinition = "path/to/definition-one.dmn";
ZipInputStream inputStream = new ZipInputStream(new FileInputStream(barFileName));
repositoryService.createDeployment()
.name("definition-one")
.addClasspathResource(dmnDefinition)
.deploy();
5.2. DMN选择的版本 Versioning of DMN decisions
DMN并没有版本的概念。这其实很好,因为可执行的DMN定义文件很可能已经作为你的开发项目的一部分,保存在版本管理系统仓库中了(例如Subversion,Git,或者Mercurial)。在部署时,会创建DMN定义的版本。在部署时,Flowable会在保存至Flowable数据库前,为decision 指定版本。
对于DMN定义中的每个DMN选择,下列步骤都会执行,以初始化key 、version 、name 与id 参数:
4 | <definitions id="myDefinitions" >
<decision id="myDecision" name="My important decision" >
<decisionTable id="decisionTable1" hitPolicy="FIRST" >
4 | <definitions id="myNewDefinitions" >
<decision id="myNewDecision" name="My important decision" >
<decisionTable id="decisionTable1" hitPolicy="FIRST" >
13 | <definitions xmlns="http://www.omg.org/spec/DMN/20151101"
namespace="http://www.flowable.org/dmn"
name="DetermineDiscount">
<decision id="DET_DISC_1" name="DetermineDiscount">
<decisionTable id="determineDiscountTable1" hitPolicy="FIRST">
</decisionTable>
</decision>
</definitions>
6.3. 创建一个DMN定义 Creating a DMN definition
可以使用文本编辑器创建DMN定义。但是在这个例子中,我们使用Flowable modeler中提供的选择表编辑器创建。
我们将实现一个非常简单的用例:根据用户分类,决定折扣比例。
在Flowable modeler中打开选择表界面。
44 | <definitions xmlns="http://www.omg.org/spec/DMN/20151101" id="definition_052249e2-f35d-11e6-9c45-0242ac120005" name="Determine Discount" namespace="http://www.flowable.org/dmn">
<decision id="DET_DISC_1" name="Determine Discount">
<decisionTable id="decisionTable_052249e2-f35d-11e6-9c45-0242ac120005" hitPolicy="FIRST">
<input label="Customer Category">
<inputExpression id="inputExpression_5">
<text>customercat</text>
</inputExpression>
</input>
<output id="outputExpression_6" label="Discount Percentage" name="discountperc" typeRef="number"></output>
<inputEntry id="inputEntry_5_1">
<text>== "BRONZE"</text>
</inputEntry>
<outputEntry id="outputEntry_6_1">
<text>5</text>
</outputEntry>
</rule>
<inputEntry id="inputEntry_5_2">
<text>== "SILVER"</text>
</inputEntry>
<outputEntry id="outputEntry_6_2">
<text>10</text>
</outputEntry>
</rule>
<inputEntry id="inputEntry_5_3">
<text>== "GOLD"</text>
</inputEntry>
<outputEntry id="outputEntry_6_3">
<text>20</text>
</outputEntry>
</rule>
<inputEntry id="inputEntry_5_4">
<text></text>
</inputEntry>
<outputEntry id="outputEntry_6_4">
<text>0</text>
</outputEntry>
</rule>
</decisionTable>
</decision>
</definitions>
7.1. Flowable DMN REST一般原则 General Flowable DMN REST principles
7.1.1. 安装与认证 Installation and Authentication
Flowable DMN在DMN引擎中包含了REST API,可以通过在servlet容器如Apache Tomcat中,部署flowable-app-rest.war文件来安装。但是也可以在其他的web应用中使用,只要在你的应用中包含这些servlet及其mapping,并在classpath中添加所有flowable-dmn-rest模块的依赖即可。
默认情况下Flowable DMN引擎连接至一个H2内存数据库。可以修改WEB-INF/classes文件夹下的db.properties文件中的数据库设置。DMN REST API使用JSON格式 (http://www.json.org) ,基于Spring MVC (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html) 构建。
默认情况下,所有REST资源都需要有一个有效的Flowable已认证用户。我们使用基础HTTP访问认证。因此在请求时,可以在HTTP头添加 Authorization: BasicXXX== ,也可以在请求url中包含用户名与密码(例如http://username:password@localhost:8080/xyz)。
建议使用基础认证时,同时使用HTTPS。
7.1.2. 配置 Configuration
Flowable REST web应用(flowable-app-rest)使用Spring Java Configuration来启动Flowable引擎、定义基础认证安全使用Spring security,以及为特定的变量处理定义变量转换。可以修改WEB-INF/classes目录下的engine.properties文件,定义少量参数。如果需要高级配置选项,可以在flowable-custom-context.xml文件中覆盖默认的Spring bean,这个文件也在WEB-INF/classes目录下。该文件中已经以注释形式提供了示例配置。也可以在这里重载默认的RestResponseFactories。对于DMN引擎来说,就是定义一个新的命名为dmnResponsefactory的Spring bean,使用自定义实现类。
7.1.3. 在Tomcat中使用 Usage in Tomcat
由于Tomcat中的默认安全参数,默认不能使用已转义斜线符(%2F 与%5C )(返回400结果)。这可能会影响部署资源与其数据URL,因为URL可能隐含已转义斜线符。
当发现非预期的400结果时,设置下列系统参数:
-Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true
最佳实践是(post/put JSON时),在下面描述的HTTP请求中,永远将Accept与Content-Type头设置为application/json。
7.1.4. 方法与返回码 Methods and return-codes
|