Drools 5.1.1_DOC (51)
Drools 5.1.1_DOC_Drools Flow
第6章 持久化
Drools 流允许持久化存储某些信息,即,流程运行时状态、流程定义和历史信息。
只 要一个流程被启动,一个流程实例被创建,其表示在那个特定上下文中执行的流程。例如,在执行一个指定如何处理销售订单的流程时,为每次销售请求创建一个流 程实例。流程实例表示在那个特定上下文中当前执行的状态,并且包含与那个流程相关的所有信息。注意,它只包含在一段时间后流程实例继续执行需要的最小运行 时状态,但是它不包括那个流程实例的历史信息,如果那些信息不再需要在该流程实例中。
一 个执行流程的运行时状态可以被持久化,例如,在一个数据库中。这允许在突发故障的情况下,恢复所有运行流程的执行状态,或者暂时从内存中删除运行实例,并 在一段时间后恢复它们。Drools 流允许你插入不同的持久化策略。默认时,如果你没有配置流程引擎,那么流程实例不会做持久化。
Drools 流提供了一个二进制持久化机制,它允许你以一个二进制数据集保存一个流程实例。这种方式,所有运行流程实例的状态始终可以被存储在一个持久化的位置。注 意,这些二进制数据集通常相对比较小,因为它们只包含了流程实例的最小执行状态。对于一个简单地流程实例,它通常包含一个或多个节点实例,即,正在执行的 任何节点, 以及可能的一些变量的值。
在 流程引擎的执行期间,一个流程实例的状态在所谓的“安全点”被存储。只要一个流程实例在启动或从一个等待状态继续后正被执行,引擎继续进行,直到不再有动 作被执行。在这点时,引擎已经达到下一个安全状态,那么该流程实例和所有其他可能被影响的流程实例的状态被持久化保存。
默 认时,引擎不持久化保存运行时数据。然而,通过增加一个配置文件和必要依赖包,配置引擎做这个是相当简单的。持久化本身是基于Java Persistence API (JPA) ,因此可以使用几种持久化机制。默认时,我们使用Hibernate,但也可以随意使用其他替代品。在底层使用H2数据库来存储数据 ,但是你也可以选择自己的替代品。
第一,你需要添加必要的依赖包到你的类路径。 如果你使用 Eclipse IDE,你可以通过添加jar文件到你的Drools运行时间目录(参考“Drools Eclipse IDE 功能”章节),或者通过手工添加这些依赖包到你的项目。首先,你需要 jar文件 drools-persistence-jpa.jar,因为它包含的代码用于随时保存运行时状态。其次,你也需要各种各样的其他依赖包,取决于持久化解 决方案和你正使用的数据库。对于默认组合,作为JPA持久化提供者的Hibernate ,H2数据库,基于JTA事务管理的Bitronix,需要以下列出的依赖包:
-
drools-persistence-jpa (org.drools)
-
persistence-api-1.0.jar (javax.persistence)
-
hibernate-entitymanager-3.4.0.GA.jar (org.hibernate)
-
hibernate-annotations-3.4.0.GA.jar (org.hibernate)
-
hibernate-commons-annotations-3.1.0.GA.jar (org.hibernate)
-
hibernate-core-3.3.0.SP1.jar (org.hibernate)
-
dom4j-1.6.1.jar (dom4j)
-
jta-1.0.1B.jar (javax.transaction)
-
btm-1.3.2.jar (org.codehaus.btm)
-
javassist-3.4.GA.jar (javassist)
-
slf4j-api-1.5.2.jar (org.slf4j)
-
slf4j-jdk14-1.5.2.jar (org.slf4j)
-
h2-1.0.77.jar (com.h2database)
-
commons-collections-3.2.jar (commons-collections)
第 二,要随时保存引擎的状态,你需要配置 Drools引擎。最容易的做法是根据一个知识库、一个知识会话的配置(如果需要)和环境,使用JPAKnowledgeService来创建你的知识会 话。环境包含一个对你的Entity Manager Factory(实体管理器工厂)的一个引用。
// create the entity manager factory and register it in the environment
EntityManagerFactory emf = Persistence.createEntityManagerFactory( "org.drools.persistence.jpa" );
Environment env = KnowledgeBaseFactory.newEnvironment();
env.set( EnvironmentName.ENTITY_MANAGER_FACTORY, emf );
// create a new knowledge session that uses JPA to store the runtime state
StatefulKnowledgeSession ksession = JPAKnowledgeService.newStatefulKnowledgeSession( kbase, null, env );
int sessionId = ksession.getId();
// invoke methods on your method here
ksession.startProcess( "MyProcess" );
ksession.dispose();
你也可以使用JPAKnowledgeService,根据特定的会话id重新创建一个会话:
// recreate the session from database using the session id
ksession = JPAKnowledgeService.loadStatefulKnowledgeSession( sessionId, kbase, null, env );
注意,我们只保存在一段时间后流程实例继续执行需要的最小运行时状态。这意味着,例如,如果信息不再相关,或者已经完成或中断了的流程实例从数据库中被删除,它不包含有关已经执行节点的信息。如果你希望搜索相关历史信息,你应该使用历史记录,如后面解释的一样。
默 认时,drools-persistence-jpa.jar包含了一个配置JPA使用Hibernate和H2数据库的配置文件,名为 persistence.xml,在META-INF目录中,如下所示。如果你想改变它们,你需要覆盖这些默认配置,通过添加你自己的 persistence.xml在你的类路径,其优先于在drools-persistence-jpa.jar中默认的一个。有关如何做它的详情,请参 考JPA和Hibernate文档。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence
version="1.0"
xsi:schemaLocation=
"http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd
http://java.sun.com/xml/ns/persistence/orm
http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="org.drools.persistence.jpa">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/processInstanceDS</jta-data-source>
<class>org.drools.persistence.session.SessionInfo</class>
<class>org.drools.persistence.processinstance.ProcessInstanceInfo</class>
<class>org.drools.persistence.processinstance.ProcessInstanceEventInfo</class>
<class>org.drools.persistence.processinstance.WorkItemInfo</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.max_fetch_depth" value="3"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.transaction.manager_lookup_class"
value="org.hibernate.transaction.BTMTransactionManagerLookup"/>
</properties>
</persistence-unit>
</persistence>
这个配置文件指向一个名为"jdbc/processInstanceDS"的数据源。下面的 Java片段可以被用来设置这个数据源,在那儿我们使用基于文件的H2数据库。
PoolingDataSource ds = new PoolingDataSource();
ds.setUniqueName("jdbc/processInstanceDS");
ds.setClassName("org.h2.jdbcx.JdbcDataSource");
ds.setMaxPoolSize(3);
ds.setAllowLocalTransactions(true);
ds.getDriverProperties().put("user", "sa");
ds.getDriverProperties().put("password", "sasa");
ds.getDriverProperties().put("URL", "jdbc:h2:file:/NotBackedUp/data/process-instance-db");
ds.init();
只要你没有在你的应用程序内提供事务边界,引擎会自动在引擎中以一个独立事务执行每个方法调用。如果能接受这种行为,你不需要做任何其他事情了。然而,你也可以指定你自己的事务边界。这允许你,例如,合并多个命令到一个事务内。
在开始使用用户定义的事件之前,你需要注册一个事务管理器。下面的例子代码使用了Bitronix事务管理器 。紧接着,我们使用了Java Transaction API (JTA) 来指定事务边界,如下所示:
// create the entity manager factory and register it in the environment
EntityManagerFactory emf = Persistence.createEntityManagerFactory( "org.drools.persistence.jpa" );
Environment env = KnowledgeBaseFactory.newEnvironment();
env.set( EnvironmentName.ENTITY_MANAGER_FACTORY, emf );
env.set( EnvironmentName.TRANSACTION_MANAGER, TransactionManagerServices.getTransactionManager() );
// create a new knowledge session that uses JPA to store the runtime state
StatefulKnowledgeSession ksession = JPAKnowledgeService.newStatefulKnowledgeSession( kbase, null, env );
// start the transaction
UserTransaction ut = (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" );
ut.begin();
// perform multiple commands inside one transaction
ksession.insert( new Person( "John Doe" ) );
ksession.startProcess( "MyProcess" );
ksession.fireAllRules();
// commit the transaction
ut.commit();
6.2. 流程定义
流 程定义文件通常以XML格式编写。在部署期间,这些文件可以轻松地被存储在一个文件系统上。然而,每当你希望在产品中让一个或多个引擎可访问你的知识,我 们建议使用一个知识信息库(knowledge repository ),逻辑上集中你的在一个或多个知识信息库中的知识。
Guvnor 正是提供这个的一个子项目。它由一个信息库(repository )构成,用于存储不同类型的知识,不仅有流程定义,还有规则、对象模型等等。它允许轻松地使用WebDAV检索这个知识 ,或者通过采用一个知识代理(Knowledge Agent ),在创建一个知识库(Knowledge Base, )时,自动从Guvnor下载信息,并且提供了一个网页应用程序,允许业务用户浏览和可以更新在该知识信息库(knowledge repository )中的信息。有关如何做它的详情,请查看Drools Guvnor文档。
在 很多情况下,存储有关流程实例的执行信息是有用的(假设没有必要),以便于事后使用这个信息,例如,验证一个特殊流程实例已经执行了什么动作,或者监视和 分析一个特殊流程的功效。存储历史信息在一个运行时数据库(runtime database )通常不是一个好主意,因为这会导致不断增长的运行时数据,并且监视和分析的查询会影响你的运行时引擎的性能。这就是为什么有关流程实例执行的历史信息被 分开存储。
这种执行信息的历史记录由执行期间的流程引擎根据产生的事件被创建。Drools运行时引擎提供了一个通用机制来侦听不同类型的事件。根据这些事件,可以轻松地提取必要的信息,并做持久化,如在一个数据库中。使用过滤器可以只存储你查找到的相关信息。
drools-bam模块包含一个事件侦听器,使用Hibernate存储相关流程信息在一个数据库中。该数据库包含两个表,一个用于流程实例信息,一个用于节点实例信息(见下图):
-
ProcessInstanceLog::它列出了流程实例id、流程(定义)id、以及所有流程实例的开始和(如果可应用)结束日期。
-
NodeInstanceLog: 这个表包含有关在每个流程实例内部实际执行的那个节点的更详细的信息。只要从它的输入路线之一进入一个节点实例,或者经过它的输出线路之一退出,信息就被 存储在这个表中。为此,它存储流程实例id、正在执行的流程实例的流程id、 节点实例id、以及谈及的节点实例的相应的节点id(在流程定义中)、事件的类型(0=进入,1=退出)和事件日期。

要象这样记录流程历史信息在一个数据库,你需要象这样注册日志器在你的会话中(或工作内存):
StatefulKnowledgeSession ksession = ...;
WorkingMemoryDbLogger logger = new WorkingMemoryDbLogger(ksession);
// invoke methods one your session here
logger.dispose();
注 意,这个日志器就像任何其他审计日志器一样,这意味着你可以通过调用方法addFilter添加一个或多个过滤器,确保相关的信息才被存储在数据库中。只 有通过你的所有过滤器接受的信息才会出现在数据库。当不再需要日志器时,你应该处置掉( dispose )该日志器 。
要指定存储信息的数据库,修改文件hibernate.cfg.xml。 默认时,它使用常驻内存数据库(H2)。如果你不知道如何做它,请参考Hibernate文档。
可 以轻松地查询所有这些信息,并且已被用于许多不同的用例中,从为一个特定流程创建一个历史记录,到分析一个特定流程的所有实例的性能。 类 ProcessInstanceDbLog(在包org.drools.process.audit中)展示了一些,关于如何检索所有流程实例 ,一个特定流程实例(通过id),一个特定流程的所有流程实例,一个特定流程实例的所有节点实例,等等的例子。当然你也可以轻松地创建自己的 Hibernate查询,或者直接访问在该数据库中的信息。
默认时,审计日志使用H2常驻内存数据库,在启动时它会被重新创建。通过包括你自己的配置文件hibernate.cfg.xml,你可以改变这个默认设置。它允许你,例如,改变底层数据库,等等。有关如何做这的详情,请参考Hibernate 文档。

浙公网安备 33010602011771号