星期日, 8月 20, 2006

JBoss jBPM(Workflow Management Engine)備忘記

JBoss jBPM (JAVA Business Process Management) 為開源工作流程引擎,
具有非常高的彈性及可擴展性, 可使用圖形介面事先定義工作流程序述,
提供非同步, 排程, 自動觸發動作等功能,


JBoss jBPM 可以與任何的資料庫集成, 並能嵌入於任何的JAVA企業應用系統.


開始備忘記:
[1] 安裝 jdk 5
[2] 安裝 JBoss jBPM server
[3] 安裝 Eclipse WTP
[4] 安裝 JBoss IDE
[5] 建立第一個 jBPM project


[1] 安裝 jdk 5:
下載 jdk-1_5_0_07-nb-5_0-win-ml.exe
http://java.sun.com/j2se/1.5.0/download-netbeans.html
安裝至 D:\jdk1.5.0_07
新增環境變數 JAVA_HOME=D:\jdk1.5.0_07
D:\jdk1.5.0_07\bin 加入至 PATH 中
D:\jdk1.5.0_07\lib\dt.jar 及 D:\jdk1.5.0_07\lib\tools.jar 加入至 CLASSPATH 中
執行 D:\>java -version
輸出 java version "1.5.0_07" 即安裝成功.


[2] 安裝 JBoss jBPM server:
下載 jbpm-starters-kit-3.1.2.zip
http://www.jboss.com/products/jbpm/downloads
http://superb-west.dl.sourceforge.net/sourceforge/jbpm/jbpm-starters-kit-3.1.2.zip
解壓縮至 D:\jboss_jbpm
D:\jboss_jbpm\readme.html 介紹每個目錄的功能.
這裡只需要關心 D:\jboss_jbpm\jbpm-server 目錄, 這是 JBoss jBPM server
執行 D:\jboss_jbpm\jbpm-server\start.bat 啟動 JBoss jBPM server
JBoss jBPM server 已提供了一個 JSF 的 web sale order 的例子.
進入 http://localhost:8080/jbpm/
隨便選一個 user 登入, 就可以測試預設的 web sale order workflow.


如下圖所示


官方的介面使用教學:
http://wiki.jboss.org/wiki/Wiki.jsp?page=JbpmGettingStarted


[3] 安裝 Eclipse WTP:
下載 wtp-all-in-one-sdk-R-1.5.0-200606281455-win32.zip
http://www.eclipse.org/webtools/
http://www.eclipse.org/downloads/download.php?file=/webtools/downloads/drops/R-1.5.0-200606281455/wtp-all-in-one-sdk-R-1.5.0-200606281455-win32.zip
解壓至 D:\eclipse_wtp


[4] 安裝 JBoss IDE:
Eclipse:Help -> Software Updates -> Find and Install -> Search for new features to install
New Remote Site
Name: JBOSS IDE
URL: http://download.jboss.org/jbosside/updates/stable
選擇最新的版本然後安裝.


[5] 建立第一個 jBPM project:
Eclipse:
File -> New -> Other -> JBoss jBPM -> Process Project
Project Name: First_jBPM -> Next -> Finish
預設會產生一整套測試檔案, 這備忘記就是對這些檔案作簡介.
如果想更深入了解 jBPM , 查看官方的文檔是最好的方法.


檔案結構如下所示


右鍵點選 gpd.xml -> Open With -> Text Editor , 就會出現如下所示代碼
<!---------------------- gpd.xml --------------------->
<?xml version="1.0" encoding="UTF-8"?>


<process-diagram name="simple" width="469" height="438">
<node name="start" x="150" y="25" width="140" height="40">
<transition name="to_state"/>
</node>
<node name="first" x="150" y="125" width="140" height="40">
<transition name="to_end"/>
</node>
<node name="end" x="150" y="225" width="140" height="40"/>
</process-diagram>
<!---------------------- gpd.xml --------------------->
這是 graphical process designer 的描述檔案.
這裡只是簡單描述三個 nodes 的位置: start , first, end


右鍵點選 processdefinition.xml -> Open With -> Text Editor , 就會出現如下所示代碼
<!---------------------- processdefinition.xml --------------------->
<?xml version="1.0" encoding="UTF-8"?>


<process-definition
xmlns="urn:jbpm.org:jpdl-3.1"
name="simple">
<start-state name="start">
<task>
<controller>
<variable name="color" />
<variable name="size" />
</controller>
</task>
<transition name="to_state" to="first">
<action name="action" class="com.sample.action.MessageActionHandler">
<message>Going to the first state!</message>
</action>
</transition>
</start-state>
<state name="first">
<transition name="to_end" to="end">
<action name="action" class="com.sample.action.MessageActionHandler">
<message>About to finish!</message>
</action>
</transition>
</state>
<end-state name="end"></end-state>
</process-definition>
<!---------------------- processdefinition.xml --------------------->
workflow engine 將根據此流程檔運作.
<start-state name="start"> 流程初始點.


<transition name="to_state" to="first">
<action name="action" class="com.sample.action.MessageActionHandler">
<message>Going to the first state!</message>
</action>
</transition>
這裡描述初始點將過渡至 first 點.
而過渡至 first 點時會觸發 com.sample.action.MessageActionHandler [後面會介紹]
並設定此 ActionHandler 的 message 為 "Going to the first state!"


<state name="first">
<transition name="to_end" to="end">
<action name="action" class="com.sample.action.MessageActionHandler">
<message>About to finish!</message>
</action>
</transition>
</state>

這裡描述 first 點. 通過此點將過渡至 end 點.
而過渡至 end 點時會觸發 com.sample.action.MessageActionHandler [後面會介紹]
並設定此 ActionHandler 的 message 為 "About to finish!"


<end-state name="end"> 流程結速點.



右鍵點選 processdefinition.xml -> Open With -> jBPM Graphical Process Designer
然後點 Diagram , 就會出現如下圖所示


可以使用 GPD (Graphical Process Designer) 來繪製 workflow
為了簡化備忘記, 參考以下官方教學文檔
http://docs.jboss.com/jbpm/v3/gpd



/*------------------ MessageActionHandler.java -------------------*/
package com.sample.action;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
public class MessageActionHandler implements ActionHandler {
// 這是 Listener, 觸發這個 Action 將執行 execute method
private static final long serialVersionUID = 1L;

String message;

public void execute(ExecutionContext context) throws Exception {
context.getContextInstance().setVariable("message", message);
// 這裡將上面觸發的 message 加入到該點的 message property
}


}
/*------------------ MessageActionHandler.java -------------------*/



/*------------------ SimpleProcessTest.java -------------------*/
package com.sample;
import java.io.FileInputStream;
import junit.framework.TestCase;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
public class SimpleProcessTest extends TestCase {


public void testSimpleProcess() throws Exception {


FileInputStream fis = new FileInputStream("processes/simple/processdefinition.xml");
ProcessDefinition processDefinition = ProcessDefinition.parseXmlInputStream(fis);
// 讀取 processdefinition.xml 的流程定義檔

assertNotNull("Definition should not be null", processDefinition);
// 測試這個檔案並不為 null


ProcessInstance instance = new ProcessInstance(processDefinition);
// 建立處理流程檔實例

assertEquals(
"Instance is in start state",
instance.getRootToken().getNode().getName(),
"start");
// 測試現在正處於 start 點

assertNull(
"Message variable should not exist yet",
instance.getContextInstance().getVariable("message"));
// 測試 start 點的 message 為 null


instance.signal();
// 呼叫 signal() 表示此點處理完成, 將跳至下一點
// 由於上面設定了 ActionHandler, 將會設定 message 為 "Going to the first state!"


assertEquals(
"Instance is in first state",
instance.getRootToken().getNode().getName(),
"first");
// 測試現在正處於 first 點

assertEquals(
"Message variable contains message",
instance.getContextInstance().getVariable("message"),
"Going to the first state!");
// 由於已經觸發 MessageActionHandler,
// 測試此點 message property 為 "Going to the first state!"


instance.signal();
// 呼叫 signal() 表示此點處理完成, 將跳至下一點
// 由於上面設定了 ActionHandler, 將會設定 message 為 "About to finish!"


assertEquals(
"Instance is in end state",
instance.getRootToken().getNode().getName(),
"end");
// 測試現在正處於 end 點

assertTrue("Instance has ended", instance.hasEnded());
// 測試現在是最後終點

assertEquals(
"Message variable is changed",
instance.getContextInstance().getVariable("message"),
"About to finish!");
// 由於已經觸發 MessageActionHandler,
// 測試此點 message property 為 "About to finish!"


}
}
/*------------------ SimpleProcessTest.java -------------------*/


點選 SimpleProcessTest.java -> Run As -> JUnit Test
如下所示即測試成功


Console 輸出為:
00:33:44,597 [main] INFO JbpmConfiguration : using jbpm configuration resource 'jbpm.cfg.xml'
00:33:44,644 [main] DEBUG JbpmConfiguration : loading defaults in jbpm configuration
00:33:45,269 [main] DEBUG ObjectFactoryImpl : adding object info 'default.jbpm.context'
00:33:45,269 [main] DEBUG ObjectFactoryImpl : adding object info 'resource.hibernate.cfg.xml'
00:33:45,269 [main] DEBUG ObjectFactoryImpl : adding object info 'resource.business.calendar'
00:33:45,285 [main] DEBUG ObjectFactoryImpl : adding object info 'resource.default.modules'
00:33:45,285 [main] DEBUG ObjectFactoryImpl : adding object info 'resource.converter'
00:33:45,285 [main] DEBUG ObjectFactoryImpl : adding object info 'resource.action.types'
00:33:45,285 [main] DEBUG ObjectFactoryImpl : adding object info 'resource.node.types'
00:33:45,285 [main] DEBUG ObjectFactoryImpl : adding object info 'resource.parsers'
00:33:45,285 [main] DEBUG ObjectFactoryImpl : adding object info 'resource.varmapping'
00:33:45,285 [main] DEBUG ObjectFactoryImpl : adding object info 'jbpm.msg.wait.timout'
00:33:45,285 [main] DEBUG ObjectFactoryImpl : adding object info 'jbpm.byte.block.size'
00:33:45,285 [main] DEBUG ObjectFactoryImpl : adding object info 'mail.smtp.host'
00:33:45,285 [main] DEBUG ObjectFactoryImpl : adding object info 'jbpm.task.instance.factory'
00:33:45,285 [main] DEBUG ObjectFactoryImpl : adding object info 'jbpm.variable.resolver'
00:33:45,285 [main] DEBUG ObjectFactoryImpl : adding object info 'jbpm.mail.address.resolver'
00:33:45,285 [main] DEBUG JbpmConfiguration : loading specific configuration...
00:33:45,753 [main] DEBUG JpdlParser$JpdlEntityResolver : resolving schema reference publicId(null) systemId(
http://jbpm.org/jpdl-3.1.xsd)
00:33:45,753 [main] DEBUG JpdlParser$JpdlEntityResolver : providing input source to local 'jpdl-3.1.xsd' resource
00:33:46,019 [main] WARN JpdlXmlReader : process xml warning: warning: no swimlane or assignment specified for task '<task xmlns="urn:jbpm.org:jpdl-3.1" blocking="false" signalling="true" priority="normal">
<controller config-type="field">
<variable name="color" access="read,write"/>
<variable name="size" access="read,write"/>
</controller>
</task>'
00:33:46,081 [main] DEBUG GraphElement : event 'process-start' on 'ProcessDefinition(simple)' for 'Token(/)'
00:33:46,081 [main] DEBUG GraphElement : event 'before-signal' on 'StartState(start)' for 'Token(/)'
00:33:46,081 [main] DEBUG GraphElement : event 'node-leave' on 'StartState(start)' for 'Token(/)'
00:33:46,081 [main] DEBUG GraphElement : event 'transition' on 'Transition(to_state)' for 'Token(/)'
00:33:46,081 [main] DEBUG GraphElement : executing action 'action[action]'
00:33:46,113 [main] DEBUG VariableContainer : create variable 'message' in 'TokenVariableMapc4aad3' with value 'Going to the first state!'
00:33:46,159 [main] DEBUG Converters : adding converter 'D', 'org.jbpm.context.exe.converter.DoubleToStringConverter'
00:33:46,159 [main] DEBUG Converters : adding converter 'C', 'org.jbpm.context.exe.converter.CharacterToStringConverter'
00:33:46,159 [main] DEBUG Converters : adding converter 'B', 'org.jbpm.context.exe.converter.BooleanToStringConverter'
00:33:46,159 [main] DEBUG Converters : adding converter 'Y', 'org.jbpm.context.exe.converter.BytesToByteArrayConverter'
00:33:46,159 [main] DEBUG Converters : adding converter 'A', 'org.jbpm.context.exe.converter.DateToLongConverter'
00:33:46,159 [main] DEBUG Converters : adding converter 'R', 'org.jbpm.context.exe.converter.SerializableToByteArrayConverter'
00:33:46,175 [main] DEBUG Converters : adding converter 'I', 'org.jbpm.context.exe.converter.IntegerToLongConverter'
00:33:46,175 [main] DEBUG Converters : adding converter 'H', 'org.jbpm.context.exe.converter.ShortToLongConverter'
00:33:46,175 [main] DEBUG Converters : adding converter 'G', 'org.jbpm.context.exe.converter.FloatToDoubleConverter'
00:33:46,191 [main] DEBUG Converters : adding converter 'F', 'org.jbpm.context.exe.converter.FloatToStringConverter'
00:33:46,191 [main] DEBUG Converters : adding converter 'E', 'org.jbpm.context.exe.converter.ByteToLongConverter'
00:33:46,206 [main] DEBUG GraphElement : event 'node-enter' on 'State(first)' for 'Token(/)'
00:33:46,206 [main] DEBUG GraphElement : event 'after-signal' on 'StartState(start)' for 'Token(/)'
00:33:46,206 [main] DEBUG GraphElement : event 'before-signal' on 'State(first)' for 'Token(/)'
00:33:46,206 [main] DEBUG GraphElement : event 'node-leave' on 'State(first)' for 'Token(/)'
00:33:46,222 [main] DEBUG GraphElement : event 'transition' on 'Transition(to_end)' for 'Token(/)'
00:33:46,222 [main] DEBUG GraphElement : executing action 'action[action]'
00:33:46,222 [main] DEBUG VariableContainer : update variable 'message' in 'TokenVariableMapc4aad3' to value 'About to finish!'
00:33:46,222 [main] DEBUG GraphElement : event 'node-enter' on 'EndState(end)' for 'Token(/)'
00:33:46,222 [main] DEBUG GraphElement : event 'process-end' on 'ProcessDefinition(simple)' for 'Token(/)'
00:33:46,222 [main] DEBUG GraphElement : event 'after-signal' on 'State(first)' for 'Token(/)'


如下圖所示:


這裡只是簡單的介紹如何開發 workflow system,
並沒有實作用戶介面, 可集成至 JSP, JSF 或 Tapestry 實作.


如果需要 embed 到其他的系統, 可參考官方文檔:
http://docs.jboss.com/jbpm/v3/userguide/deployment.html


由於本人亦使用過 OpenWFE [Sourceforge workflow engine 熱門項目]
OpenWFE 功能比較強, 文檔亦很多, 也比較複雜.
但 JBoss jBPM 有 JBossIDE 支持, 感覺上比較容易開發.
總結是現在沒有一套 workflow engine 能滿足用戶的所有需求.


OpwnWFE 官方網頁:
http://www.openwfe.org,


JBoss jBPM 用戶手冊:
http://docs.jboss.com/jbpm/v3/userguide/


JBoss jBPM 官方文檔:
http://www.jboss.com/products/jbpm/docs


JBoss jBPM wiki:
http://wiki.jboss.org/wiki/Wiki.jsp?page=JbpmWiki


workflow pattern:
http://is.tm.tue.nl/research/patterns/


沒有留言: