第 13 章 執行模式
這裡有三種基本的流程執行模式:對象,持久化和嵌入. 對於持久化和嵌入執行模式, 流程執行必須在一個事務中執行.在那種情況, 流程執行必須放在一個環境的內部. 環境將用來綁定流程執行,更新到一個應用事務的事務中. 環境可以被用來綁定,比如一個JDBC連接, JTA,BMT,Spring事務等等.
13.1. 對象執行模式
對象執行模式是使用流程虛擬機的最簡單形式. 這意味著通過客戶端API直接使用流程定義和執行對象. 讓我們通過一個例子演示這個. 我們通過創建一個ClientProcessDefinition開始,看起來像這樣:
對象執行模式是使用流程虛擬機的最簡單形式. 這意味著通過客戶端API直接使用流程定義和執行對象. 讓我們通過一個例子演示這個. 我們通過創建一個ClientProcessDefinition開始,看起來像這樣:
圖 13.1. 貸款流程
ClientProcessDefinition processDefinition = ProcessFactory.build("loan") .activity("submit loan request").initial().behaviour(AutomaticActivity.class) .transition().to("evaluate") .activity("evaluate").behaviour(WaitState.class) .transition("approve").to("wire money") .transition("reject").to("end") .activity("wire money").behaviour(AutomaticActivity.class) .transition().to("archive") .activity("archive").behaviour(WaitState.class) .transition().to("end") .activity("end").behaviour(WaitState.class) .done(); |
ProcessFactory是一個幫助類, 為構建一個表現為流程定義的對象圖提供方便. AutomaticActivity是一個通過活動, 沒有任何操作發生,WaitState會等到外部signal發生. 這兩個活動實現都會在後面討論更深.
processDefinition對象作為一個工廠,為流程實例對象. 一個流程實例表現為流程定義的一個執行. 更準確的說,流程實例是執行的主路徑.
ClientExecution execution = processDefinition.startProcessInstance(); |
一個流程實例自己也是一個Execution. 潛在的,一個執行可以擁有子執行 表現執行的同步路徑.
execution可以看做是一個狀態機, 在流程定義里像描述一樣操作.啟動一個流程實例意思是 流程定義的初始節點被執行. 這是一個自動活動,執行會執行到evaluate活動. evaluate活動是一個等待狀態. 當執行到達evaluate活動,startProcessInstance方法 會返回並等待一個外部signal使用signal方法提供. 在startProcessInstance之後,我們可以證實 如果執行定位在evaluate活動.
assertEquals("evaluate", execution.getActivityName()); |
為了讓流程執行得更遠,我們提供一個外部觸發器使用 signal方法.執行的結果會被作為 signalName參數給與,像這樣:
execution.signal("approve"); |
WaitState活動實現會根據給出的signalName 選擇轉移.執行將執行自動活動wire money 然後在進入等待狀態archive后 返回.
assertEquals("archive", execution.getActivityName()); |
當執行在archive活動等待時,默認的signal會讓它 選擇第一個未命名的轉移.
execution.signal(); assertEquals("end", execution.getActivityName()); |
流程執行在客戶端的線程中. startProcessInstance方法只在到達evaluate活動時返回. 換句話說,ClientProcessDefinition.startProcessInstance和 ClientExecution.signal方法會一直堵塞直到 下一個等待狀態的到來.
13.2. 持久化執行模式
流程虛擬機也包含hibernate映射來保存流程定義和執行 在任何資料庫中.一個特定的會話外觀叫做ExecutionService 被提供給流程執行 在這樣一個持久化環境中.
兩個配置文件應該放在classpath下:一個環境配置文件 和一個hibernate.properties文件. 一個持久化執行模式的基礎配置,在一個標準Java環境 看起來像這樣:
environment.cfg.xml:
<jbpm-configuration> <process-engine-context> <deployer-manager> <assign-file-type> <file extension=".jpdl.xml" type="jpdl" /> </assign-file-type> <parse-jpdl /> <check-process /> <check-problems /> <save /> </deployer-manager> <process-service /> <execution-service /> <management-service /> <command-service> <retry-interceptor /> <environment-interceptor /> <standard-transaction-interceptor /> </command-service> <hibernate-configuration> <properties resource="hibernate.properties" /> <mapping resource="jbpm.pvm.typedefs.hbm.xml" /> <mapping resource="jbpm.pvm.wire.hbm.xml" /> <mapping resource="jbpm.pvm.definition.hbm.xml" /> <mapping resource="jbpm.pvm.execution.hbm.xml" /> <mapping resource="jbpm.pvm.variable.hbm.xml" /> <mapping resource="jbpm.pvm.job.hbm.xml" /> <mapping resource="jbpm.jpdl.hbm.xml" /> <cache-configuration resource="jbpm.pvm.cache.xml" usage="nonstrict-read-write" /> </hibernate-configuration> <hibernate-session-factory /> <id-generator /> <types resource="jbpm.pvm.types.xml" /> <job-executor auto-start="false" /> </process-engine-context> <transaction-context> <hibernate-session /> <transaction /> <pvm-db-session /> <job-db-session /> <message-session /> </transaction-context> </jbpm-configuration> |
下一個,hibernate.properties像這樣:
hibernate.properties:
hibernate.dialect org.hibernate.dialect.HSQLDialect hibernate.connection.driver_class org.hsqldb.jdbcDriver hibernate.connection.url jdbc:hsqldb:mem:. hibernate.connection.username sa hibernate.connection.password hibernate.hbm2ddl.auto create-drop hibernate.cache.use_second_level_cache true hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider # hibernate.show_sql true hibernate.format_sql true hibernate.use_sql_comments true |
然後你可以從環境工廠中像這樣獲得服務:
EnvironmentFactory environmentFactory = new PvmEnvironmentFactory("environment.cfg.xml"); ProcessService processService = environmentFactory.get(ProcessService.class); ExecutionService executionService = environmentFactory.get(ExecutionService.class); ManagementService managementService = environmentFactory.get(ManagementService.class); |
ProcessService的責任是管理流程定義資源. 在我們可以啟動一個流程執行之前, 流程定義需要被發布到流程資源庫中. 流程定義可以使用不同的格式和不同的流程語言提供. 一個發布包含了流程定義信息,從不同的源文件中,像一個ZIP文件, 一個XML文件或一個流程定義對象. ProcessService.deploy方法會獲得一個發布 通過配置在配置文件里的所有發布器.
在這個例子中,我們通過代碼方式為發布 提供一個流程定義.
ClientProcessDefinition processDefinition = ProcessFactory.build("loan") .activity("submit loan request").initial().behaviour(AutomaticActivity.class) .transition().to("evaluate") .activity("evaluate").behaviour(WaitState.class) .transition("approve").to("wire money") .transition("reject").to("end") .activity("wire money").behaviour(AutomaticActivity.class) .transition().to("archive") .activity("archive").behaviour(WaitState.class) .transition().to("end") .activity("end").behaviour(WaitState.class) .done(); Deployment deployment = new Deployment(processDefinition); processService.deploy(deployment); |
現在流程定義的一個版本保存到資料庫中. check-version發布器會把版本1 分配給存儲的流程定義.create-id發布器 會提取idloan:1 根據流程名稱和分配的版本.
再次發布流程會導致在資料庫中創建一個新流程定義. 但是一個增加的版本數會被分配. 出於版本化的目的,如果有相同的名字, 流程定義就會相同.
推薦用戶為所有流程執行提供key的引用. 啟動一個新流程執行像這樣:
Execution execution = executionService.startExecution("loan:1", "request7836"); |
返回值是一個execution介面,防止關係的嚮導. 那是服務方法外面,事務和hibernate會話沒有保證一直打開. 實際上,上面給出的默認的配置只保證 事務和會話在服務方法執行中是打開的. 服務方法外的關係導航可能引起一個hibernate的 LazyInitializationException. 但是當前的活動名稱還可以被驗證.
assertEquals("evaluate", execution.getActivityName()); |
生成可以被獲得的id也是非常重要的. 默認的id-generator會用來生成流程定義的id 給出的key來為流程執行生成一個唯一id,像這樣:
assertEquals("loan:1/request7836", execution.getId()); |
那個id必須提供給外部觸發器 像這樣處理流程執行:
executionService.signalExecution("loan:1/request7836", "approve"); |
關於服務介面的更多信息,關於如何運行在持久化模式下, 可以在包 org.jbpm.pvm 的api doc找到.
13.3. 嵌入執行模式
嵌入執行模式意味著路程的狀態保存在 一個用戶領域對象的字元串列中,比如一個loan.
public class Loan { /** the loan process definition as a static resource */ private static ClientProcessDefinition createLoanProcess() { return processDefinition; /** exposes the process definition to the execution hibernate type */
long dbid; String customer; double amount; ClientExecution execution; /** constructor for persistence */ public Loan(String customer, double amount) { public void approve() { public void reject() { public void archiveComplete() { public String getState() { ...getters... |
如果你暫時忽略加粗部分,你可以看到這是一個沒有任何奇異的POJO. 它只是一個bean,可以保存到hibernate中. 粗體部分展示了類的實現部分,這與流程和執行相關. 流程定義或者執行都沒有暴露給 Loan類的用戶.
每個Loan對象對應一個loan流程實例. Loan類的一些方法 對應外部觸發器, 這會在Loan對象的生命周期被觸發.
接下來我們演示如何使用這個類,為了開始,我們需要一個
hibernate.cfg.xml:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property> <property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property> <property name="hibernate.connection.url">jdbc:hsqldb:mem:.</property> <property name="hibernate.connection.username">sa</property> <property name="hibernate.connection.password"></property> <property name="hibernate.hbm2ddl.auto">create</property> <property name="hibernate.show_sql">true"</property> <property name="hibernate.format_sql">true"</property> <property name="hibernate.use_sql_comments">true"</property> <mapping resource="Loan.hbm.xml"/> </session-factory> </hibernate-configuration> |
和一個
Loan.hbm.xml:
<?xml version="1.0"?< <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"< <hibernate-mapping package="org.jbpm.pvm.api.db.embedded" default-access="field"< <typedef name="execution" class="org.jbpm.pvm.internal.hibernate.ExecutionType" /> <class name="Loan" table="LOAN"< <id name="dbid"< <generator class="sequence"/> </id< <property name="execution" type="execution" /> <property name="customer" /> <property name="amount" /> </class< </hibernate-mapping< |
然後你可以在測試中像這樣使用Loan類
Configuration configuration = new Configuration(); configuration.configure(); SessionFactory sessionFactory = configuration.buildSessionFactory(); // start a session/transaction Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); Loan loan = new Loan("john doe", 234.0); session.save(loan); assertEquals("evaluate", loan.getState()); // start a new session/transaction transaction.commit(); session.close(); session = sessionFactory.openSession(); transaction = session.beginTransaction(); loan = (Loan) session.get(Loan.class, loan.getDbid()); assertEquals("evaluate", loan.getState()); loan.approve(); assertEquals("archive", loan.getState()); // start a new session/transaction transaction.commit(); session.close(); |
在執行這段代碼之後,這是在資料庫中的loan記錄:
圖 13.2. 資料庫中的貸款記錄
[火星人 ] jBPM-4.0中文開發指南-第13章 執行模式已經有693次圍觀