這篇文章主要講解了“怎么用jBPM實現Hello World”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“怎么用jBPM實現Hello World”吧!
在開始之前,首先下載和安裝jBPM。
jBPM包含一個圖形設計器工具,可用于創建在這些范例中顯示的xml文件。你可以在《下載和安裝jBPM》部分找到下載這個圖形設計器的指南。你不需要為了完成本教程而使用這個圖形設計工具。
Hello World范例
流程定義是一個有向圖,由節點(node)和轉移(transition)組成。Hello world流程有三個節點。為了看到這些代碼片段如何組合在一起,我們從一個簡單的流程開始,不使用圖形設計工具。下面的圖顯示hello world流程的圖形表示:
圖 3.1. Hello world流程圖
public void testHelloWorldProcess() { // 本方法顯示一個流程定義和此流程定義的執行。這個流程定義有三個節點:一個未命名的 // start狀態,一個狀態s和一個名為end的結束狀態。 // 下面一行將一段xml文本解析為一個流程定義ProcessDefinition。流程定義是對流程 // 的正式描述,表示為一個Java對象。 ProcessDefinition processDefinition = ProcessDefinition.parseXmlString( "<process-definition>" + " <start-state>" + " <transition to='s' />" + " </start-state>" + " <state name='s'>" + " <transition to='end' />" + " </state>" + " <end-state name='end' />" + "</process-definition>" ); // 下面一行代碼創建流程定義的一個執行。流程執行創建之后將擁有一個主執行 // 路徑(=根token),指向開始狀態節點。 ProcessInstance processInstance = new ProcessInstance(processDefinition); // 流程執行創建之后,擁有一個主執行路徑(=根token)。 Token token = processInstance.getRootToken(); // 流程執行創建之后,主執行路徑指向流程定義的開始狀態節點。 assertSame(processDefinition.getStartState(), token.getNode()); // 下面我們啟動流程執行,從缺省轉移路線離開開始狀態節點。 token.signal(); // signal方法將阻塞,直至流程執行進入一個等待狀態。 // 流程執行進入了第一個等待狀態:狀態s。所以主執行路徑現在指向狀態s。 assertSame(processDefinition.getNode("s"), token.getNode()); // 下面我們發送第二個信號。這將恢復流程執行,通過缺省的轉移路徑離開狀態s。 token.signal(); // 現在signal方法返回了,因為流程實例到達了結束狀態節點。 assertSame(processDefinition.getNode("end"), token.getNode()); }
數據庫范例
jBPM的一個基本特征是把處于等待狀態中的流程執行持久化到數據庫中的能力。下面的范例將向你展示如何將一個流程實例保存到jBPM數據庫中。本范例也暗示存在一個流程執行的上下文。下面各個方法在不同的用戶代碼片段中創建,例如,在web應用程序中的一段用戶代碼發起一個流程執行并將它持久化到數據庫中,隨后,一個消息驅動bean從數據庫中裝載這個流程實例并恢復其執行。
public class HelloWorldDbTest extends TestCase { static JbpmConfiguration jbpmConfiguration = null; static { // 像此處的范例配置文件能夠在'src/config.files'中找到。典型地配置信息存在 // 于資源文件'jbpm.cfg.xml'中,但在這里我們直接傳遞一個XML字符串形式的配置信息。 // 首先我們創建一個JbpmConfiguration靜態對象。系統中的所有線程可以使用 // 同一個JbpmConfiguration,因此我們可以安全地把它設定為靜態的。 jbpmConfiguration = JbpmConfiguration.parseXmlString( "<jbpm-configuration>" + // jbpm-context機制能夠從jbmp使用的環境服務中分離出jbpm核心引擎。 " <jbpm-context>" + " <service name='persistence' " + " factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' />" + " </jbpm-context>" + // 同樣地,jbpm使用的所有資源文件可以從jbpm.cfg.xml中引用。 " <string name='resource.hibernate.cfg.xml' " + " value='hibernate.cfg.xml' />" + " <string name='resource.business.calendar' " + " value='org/jbpm/calendar/jbpm.business.calendar.properties' />" + " <string name='resource.default.modules' " + " value='org/jbpm/graph/def/jbpm.default.modules.properties' />" + " <string name='resource.converter' " + " value='org/jbpm/db/hibernate/jbpm.converter.properties' />" + " <string name='resource.action.types' " + " value='org/jbpm/graph/action/action.types.xml' />" + " <string name='resource.node.types' " + " value='org/jbpm/graph/node/node.types.xml' />" + " <string name='resource.varmapping' " + " value='org/jbpm/context/exe/jbpm.varmapping.xml' />" + "</jbpm-configuration>" ); } public void setUp() { jbpmConfiguration.createSchema(); } public void tearDown() { jbpmConfiguration.dropSchema(); } public void testSimplePersistence() { // 在以下的三個方法調用之間,所有的數據通過數據庫傳遞。在這個單元測試中, // 這三個方法是順序執行的,因為我們要測試一個完整的流程場景。但是在現實 //中,這些方法代表對服務器的不同請求。 // 因為我們從一個干凈的、空的內存數據庫中啟動,必須首先部署流程?,F實中, // 流程部署是由流程開發者一次性完成的。 deployProcessDefinition(); // 假設當用戶在web應用程序中提交一個form的時候,我們要啟動一個流程實例…… processInstanceIsCreatedWhenUserSubmitsWebappForm(); // 隨后,當異步消息到達之后,將繼續執行流程。 theProcessInstanceContinuesWhenAnAsyncMessageIsReceived(); } public void deployProcessDefinition() { // 本測試展示一個流程定義和該流程定義的一個執行實例。這個流程定義有 // 三個節點:一個未命名的開始狀態,一個狀態s和一個名為end的結束狀態。 ProcessDefinition processDefinition = ProcessDefinition.parseXmlString( "<process-definition name='hello world'>" + " <start-state name='start'>" + " <transition to='s' />" + " </start-state>" + " <state name='s'>" + " <transition to='end' />" + " </state>" + " <end-state name='end' />" + "</process-definition>" ); // 查找在上面的過程中已配置好的POJO持久化上下文生成器。 JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { // 將流程定義部署到數據庫。 jbpmContext.deployProcessDefinition(processDefinition); } finally { // 銷毀POJO持久化上下文。 // 這包括刷新SQL,將流程定義插入到數據庫中。 jbpmContext.close(); } } public void processInstanceIsCreatedWhenUserSubmitsWebappForm() { // 本方法中的代碼應存在于一個struts的action 或JSF托管的bean中。 // 查找在上面的過程中已配置好的POJO持久化上下文生成器。 JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { GraphSession graphSession = jbpmContext.getGraphSession(); ProcessDefinition processDefinition = graphSession.findLatestProcessDefinition("hello world"); // 從數據庫中取得流程定義之后,我們可以創建該流程定義的一個執行實例, // 就像Hello world范例中一樣(后者沒有使用持久化)。 ProcessInstance processInstance = new ProcessInstance(processDefinition); Token token = processInstance.getRootToken(); assertEquals("start", token.getNode().getName()); // 下面啟動流程執行 token.signal(); // 現在流程處于狀態s。 assertEquals("s", token.getNode().getName()); // 現在流程實例被保存到數據庫中,所以流程執行的當前狀態被保存到數據庫中了。 jbpmContext.save(processInstance); // 下面的方法將從數據庫中取回流程實例,通過提供另一個外部信號恢復流程的執行。 } finally { // 銷毀POJO持久化上下文。 jbpmContext.close(); } } public void theProcessInstanceContinuesWhenAnAsyncMessageIsReceived() { // 本方法中的代碼可以是一個消息驅動bean的內容。 //查找在上面的代碼中已經配置好的POJO持久化上下文生成器 JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { GraphSession graphSession = jbpmContext.getGraphSession(); // 首先,我們需要從數據庫中取回流程實例。為了知道哪個流程實例是我們這里要使用的, // 有幾個可選方法。在這個簡單的測試案例中,最容易的方式是在整個流程實例列表中查 // 找,這將僅僅返回一個結果。所以,讓我們獲取這個流程定義。 ProcessDefinition processDefinition = graphSession.findLatestProcessDefinition("hello world"); // 現在,我們查找這個流程定義中的所有流程實例。 List processInstances = graphSession.findProcessInstances(processDefinition.getId()); // 因為我們知道,在這個單元測試測環境中只存在一個執行實例。在現實中, // processInstanceId要從到達的消息內容中提取,或來自用戶的選擇。 ProcessInstance processInstance = (ProcessInstance) processInstances.get(0); // 現在我們可以繼續流程執行了。記住流程實例將信號轉發給主執行路徑(=根token)。 processInstance.signal(); // 發出信號之后,我們知道流程執行到達了結束狀態。 assertTrue(processInstance.hasEnded()); // 現在我們可以將流程執行的狀態更新到數據庫中 jbpmContext.save(processInstance); } finally { // 銷毀POJO持久化上下文。 jbpmContext.close(); } } }
上下文(context)范例:流程變量
流程變量包含流程執行過程中的上下文信息。流程變量類似于java.util.Map,將變量名映射到值,值是java對象。流程變量作為流程實例的一部分被持久化。為了保持簡單,在此范例中我們僅僅展示與流程變量有關的API,不考慮持久化。
// 這個范例同樣從Hello world流程開始。 // 這次甚至沒有修改。 ProcessDefinition processDefinition = ProcessDefinition.parseXmlString( "<process-definition>" + " <start-state>" + " <transition to='s' />" + " </start-state>" + " <state name='s'>" + " <transition to='end' />" + " </state>" + " <end-state name='end' />" + "</process-definition>" ); ProcessInstance processInstance = new ProcessInstance(processDefinition); // 從流程實例中取得上下文實例,以處理流程變量。 ContextInstance contextInstance = processInstance.getContextInstance(); // 在流程離開開始狀態之前,我們準備在流程實例的上下文中設置一些流程變量。 contextInstance.setVariable("amount", new Integer(500)); contextInstance.setVariable("reason", "i met my deadline"); // 從現在開始,這些變量關聯到這個流程實例了?,F在這些變量可以通過用戶代碼 // 使用在這里顯示的API來訪問了,但是,也可以在action和節點實現中訪問。流 // 程變量作為流程實例的一部分,也被存儲到數據庫中。 processInstance.signal(); // 流程變量可以通過contextInstance訪問。 assertEquals(new Integer(500), contextInstance.getVariable("amount")); assertEquals("i met my deadline", contextInstance.getVariable("reason"));
任務分配范例
在下面的例子中我們展示如何將任務分配給用戶。因為jBPM工作流引擎和組織機構模型的分離,一個用于計算參與者(actor)的表達式語言總是太受限了。因此,你必須指定一個 AssignmentHandler接口的實現類來包含計算任務的參與者的過程。
public void testTaskAssignment() { // 下面的流程基于hello world流程。狀態節點被任務節點取代。任務節點是jPDL中 // 的一種節點,表示一個等待狀態,并創建在流程繼續執行之前必須完成的任務。 ProcessDefinition processDefinition = ProcessDefinition.parseXmlString( "<process-definition name='the baby process'>" + " <start-state>" + " <transition name='baby cries' to='t' />" + " </start-state>" + " <task-node name='t'>" + " <task name='change nappy'>" + " <assignment class='org.jbpm.tutorial.taskmgmt.NappyAssignmentHandler' />" + " </task>" + " <transition to='end' />" + " </task-node>" + " <end-state name='end' />" + "</process-definition>" ); // 創建流程定義的一個執行實例。 ProcessInstance processInstance = new ProcessInstance(processDefinition); Token token = processInstance.getRootToken(); // 下面開始流程執行,從缺省轉換路徑離開開始狀態。 token.signal(); // signal方法將阻塞,直至流程執行進入一個等待狀態。在這里,就是任務節點t。 assertSame(processDefinition.getNode("t"), token.getNode()); // 當流程執行到達任務節點,一個'換尿布(change nappy)'任務將被創建, // NappyAssignmentHandler被調用以決定把這個任務分配給誰。 // NappyAssignmentHandler返回'(爸爸)papa'。 // 在真實的環境中,任務通過org.jbpm.db.TaskMgmtSession類中的方法從數據 // 庫中取得。因為我們不想在本范例中包含持久化的復雜性,我們僅僅從流程實例中 // 取出第一個任務實例(我們知道在這個測試場景中只有一個任務實例)。 TaskInstance taskInstance = (TaskInstance) processInstance .getTaskMgmtInstance() .getTaskInstances() .iterator().next(); // 現在,我們檢測任務實例是否真正被指派給'papa'. assertEquals("papa", taskInstance.getActorId() ); // 現在我們假設爸爸已經完成他的任務,并把任務標記為已完成。 taskInstance.end(); // 因為這是最后(唯一)一個要完成的任務,這個任務的完成出發了流程實例的繼續執行。 assertSame(processDefinition.getNode("end"), token.getNode()); }
定制action范例
action是把你的客戶java代碼綁定到jBPM流程的一種機制。action能夠關聯到它自己的節點(如果它們在流程的圖形表示中是相關的的話)。action也能夠放置在事件(例如進入轉換、離開節點、進入節點等)之中。在這種情況下,action不是流程的圖形表示的一部分,但是,當運行時流程執行觸發了這些事件的時候,這些action將被執行。
// MyActionHandler代表一個能夠在jBPM流程執行過程中執行某些用戶代碼的類。 public class MyActionHandler implements ActionHandler { // 在每個test之前(在setUp方法中), isExecuted成員將被設置為false。 public static boolean isExecuted = false; // action將把 isExecuted設置為true,使得單元測試能夠顯示action在什么時候被執行。 public void execute(ExecutionContext executionContext) { isExecuted = true; } }
我們從在下面的范例中將要用到的action實現:MyActionHandler開始。這個action處理器實現沒有做真正有用的工作,僅僅把布爾變量isExecuted設置為true。變量isExecuted是靜態的,所以能夠同時從action處理器內部和要校驗它的值的action中訪問。
// 每個測試將在開始時設置MyActionHandler的靜態成員isExecuted為false。 public void setUp() { MyActionHandler.isExecuted = false; }
下面的例子顯示同樣的action,但是現在action被分別放在enter-node和leave-node事件之中。請注意與轉換只有一個事件不同,節點擁有多個事件類型,因此被放置到節點中的action應該放在event元素之中。
public void testTransitionAction() { // 下面的流程是hello world流程的一個變體。我們把一個action加入從狀態s到結束狀 // 態的轉換之中。本測試的目的是顯示要把java代碼集成到jBPM流程中是多么容易。 ProcessDefinition processDefinition = ProcessDefinition.parseXmlString( "<process-definition>" + " <start-state>" + " <transition to='s' />" + " </start-state>" + " <state name='s'>" + " <transition to='end'>" + " <action class='org.jbpm.tutorial.action.MyActionHandler' />" + " </transition>" + " </state>" + " <end-state name='end' />" + "</process-definition>" ); // 啟動流程定義的一個新的執行實例。 ProcessInstance processInstance = new ProcessInstance(processDefinition); // 下一個信號將導致執行離開開始狀態,到達狀態s。 processInstance.signal(); // 這里我們顯示 MyActionHandler還沒有被執行。 assertFalse(MyActionHandler.isExecuted); // ... 以及主執行路徑指向狀態s assertSame(processDefinition.getNode("s"), processInstance.getRootToken().getNode()); // 下一個信號將觸發根token的執行。該token將取得包含action的轉換, // 該action將在調用signal方法的過程中執行。 processInstance.signal(); // 這里我們可以看到在signal方法被調用的時候, MyActionHandler被執行了。 assertTrue(MyActionHandler.isExecuted); }
ProcessDefinition processDefinition = ProcessDefinition.parseXmlString( "<process-definition>" + " <start-state>" + " <transition to='s' />" + " </start-state>" + " <state name='s'>" + " <event type='node-enter'>" + " <action class='org.jbpm.tutorial.action.MyActionHandler' />" + " </event>" + " <event type='node-leave'>" + " <action class='org.jbpm.tutorial.action.MyActionHandler' />" + " </event>" + " <transition to='end'/>" + " </state>" + " <end-state name='end' />" + "</process-definition>" ); ProcessInstance processInstance = new ProcessInstance(processDefinition); assertFalse(MyActionHandler.isExecuted); // 下一個信號將導致流程執行離開開始狀態,進入狀態s。因此狀態s被進入,action被執行。 processInstance.signal(); assertTrue(MyActionHandler.isExecuted); // 重置MyActionHandler.isExecuted。 MyActionHandler.isExecuted = false; // 下一個信號將觸發流程執行離開狀態s,因此action將被再次執行。 processInstance.signal(); // 瞧…… assertTrue(MyActionHandler.isExecuted);
感謝各位的閱讀,以上就是“怎么用jBPM實現Hello World”的內容了,經過本文的學習后,相信大家對怎么用jBPM實現Hello World這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。