Abstract
這篇文章主要是介紹了jBPM 4.0的基礎架構,以及通過一個簡單的例子來讓大家知道怎么應用jBPM. 為什么選擇4.0版本呢?以后的主流版本應該是4.0版本的,所以直接寫4.0版本的了.在默認情況下,jBPM包里有個jbpm.cfg.xml,這個就是他的配置文件,我們先看下它的內容.
<jbpm-configuration>
<import resource="jbpm.default.cfg.xml" />
<import resource="jbpm.tx.jta.cfg.xml" />
<import resource="jbpm.jpdl.cfg.xml" />
<import resource="jbpm.identity.cfg.xml" />
<import resource="jbpm.jobexecutor.cfg.xml" />
</jbpm-configuration>
這里,我們再繼續看下jbpm.default.cfg.xml,看下配置文件到底是長啥樣.
<process-engine-context>
<repository-service />
<repository-cache />
<execution-service />
<history-service />
<management-service />
<identity-service />
<task-service />
<hibernate-configuration>
<cfg resource="jbpm.hibernate.cfg.xml" />
</hibernate-configuration>
...........
</process-engine-context>
<transaction-context>
<repository-session />
<db-session />
<message-session />
<timer-session />
<history-session />
<mail-session>
<mail-server>
<session-properties resource="jbpm.mail.properties" />
</mail-server>
</mail-session>
</transaction-context>
這個配置文件主要包含了"process-engine-context'和 'transaction-context'的配置.
首先是Context接口,你可以從這里存儲,獲得對象.它的接口很簡單.
Object get(String key);
<T> T get(Class<T> type);
Object set(String key, Object value);
你看可以從Context中獲取到組件,對于IOC容器來說,一般情況下都會提供一種加載的方式,比如從xml文件進行加載、從資源文件進行加載。
/**
* constructs the object.
* @param wireContext {@link WireContext} in which the object is created. This is also the {@link WireContext}
* where the object will search for other object that may be needed during the initialization phase.
* @return the constructed object.
*/
Object construct(WireContext wireContext);
/**
*called by the WireContext to initialize the specified object.
*/
void initialize(Object object, WireContext wireContext);
public interface Binding {
String getCategory();
/** does this binding apply to the given element? */
boolean matches(Element element);
/** translates the given element into a domain model java object.
* Use the parse to report problems.
*/
Object parse(Element element, Parse parse, Parser parser);
}
jbpm.cfg.xml –> jBPMConfigurationParser -> Binding –> Descriptor --> WireContext
我們不妨也看下ConfigurationTest測試.
public void testConfigurationServices() {
ProcessEngine processEngine = new Configuration()
.setXmlString(
"<jbpm-configuration>" +
" <process-engine-context>" +
" <repository-service />" +
" <execution-service />" +
" <management-service />" +
" </process-engine-context>" +
"</jbpm-configuration>"
)
.buildProcessEngine();
assertNotNull(processEngine);
assertNotNull(processEngine.getExecutionService());
assertNotNull(processEngine.getManagementService());
}
Configuration 類是jBPM的入口,你可以從 Configuration類中創建ProcessEngine,而從ProcessEngine中獲取到RepositoryService, ExecutionService, TaskService等等. Configuration類里有兩個實現類,一個是JbpmConfiguration,這是默認的jBPM自帶的Configuration解析器, 另外一個是SpringConfiguration,這個是jBPM和Spring的集成. 在這里,我們就只看下JbpmConfiguration的實現,在JbpmConfiguration類里,我們可以看到他是這么去調用Parser來 解析xml的.
protected void parse(StreamInput streamSource) {
isConfigured = true;
JbpmConfigurationParser.getInstance()
.createParse()
.pushObject(this)
.setStreamSource(streamSource)
.execute()
.checkErrors("jbpm configuration " + streamSource);
}
在這里,我們可以看到,jbpm的配置文件是有兩個元素組成的,一個是process-engine-context,另外一個是transaction-context. 其中process-engine-context里面的元素是包括了對外發布的服務, 比如repository-service, execution-service等等. 而transaction-context則包括了他的內部真正實現,比如repository-service對應repository-session. 也可以簡單的把repository-servie看做是API, repository-session看做是SPI. 這樣做的好處是,SPI的實現,對API一點影響都沒有,很大程度上提供了一個容易集成的特性.
說到這里,應該對jBPM的IOC介紹的差不多了.我自己在用JBoss IDM project[2]來實現jBPM的Identity module時.需要增加如下的配置.
<jbpm-configuration>
<process-engine-context>
<jboss-idm-identity-session-factory jndi="java:/IdentitySessionFactory" />
</process-engine-context>
<transaction-context>
<jboss-idm-identity-session realm="realm://JBossIdentity" />
</transaction-context>
</jbpm-configuration>
于是,我需要增加以下的類來完成對象的創建和實例化. JbossIdmIdentitySessionFactoryBinding,JbossIdmIdentitySessionFactoryDescriptor,JbossIdmIdentitySessionBinding, JbossIdmIdentitySessionDescriptor 然后,在jbpm.wire.bindings.xml里面注冊我們新加的Binding. 從上面我們所說的,不難看出本身這個IOC的實現機制也是很簡單的,而且也很容易擴展,如果說到時候和Spring, JBoss MC等IOC容器的集成也是很方便的.
這里,我們看下jBPM-PVM概念和架構,這也是jBPM整個項目的核心所在.
在代碼中,我們想獲得其他的組件時,或者變量時,我們總是使用Environment的API, 我們可以看下它的API的幾個重要方法.
public abstract <T> T get(Class<T> type);
/** searches an object based on type. The search doesn take superclasses of the context elements
* into account.
* @return the first object of the given type or null in case no such element was found.
*/
public abstract <T> T get(Class<T> type, String[] searchOrder);
public interface RepositoryService {
NewDeployment createDeployment();
ProcessDefinitionQuery createProcessDefinitionQuery();
...
}
public interface ExecutionService {
ProcessInstance startProcessInstanceById(String processDefinitionId);
ProcessInstance signalExecutionById(String executionId);
...
}
public interface ManagementService {
void executeJob(long jobDbid);
JobQuery createJobQuery();
}
Command 概念的引入,主要是想對所有的操作做一個封裝. 可以說上面每個Service的方法的實現都是通過實現一個Command來操作,然后通過CommandService調用到后面具體的實現. 我們先來看下Command的接口.
public interface Command<T> extends Serializable {
T execute(Environment environment) throws Exception;
}
public interface CommandService {
/**
* @throws JbpmException if command throws an exception.
*/
<T> T execute(Command<T> command);
}
CommandService 還有一個特性,就是可以配置Interceptor,比如事務就是在這一Service中來配置.看下CommandService的配置.
<command-service>
<retry-interceptor />
<environment-interceptor />
<standard-transaction-interceptor />
</command-service>
public interface ActivityBehaviour extends Serializable {
void execute(ActivityExecution execution) throws Exception;
}
public interface ExternalActivityBehaviour extends ActivityBehaviour {
//handles an external trigger.
void signal(ActivityExecution execution, String signalName, Map<String, ?> parameters) throws Exception;
}
Wait State (也就是實現ExternalActivityBehaviour)的促發,是通過Transition來完成的,來看下Transition這個接口.
public interface Transition extends ObservableElement {
/** the activity from which this transition leaves. */
Activity getSource();
/** the activity in which this transition arrives. */
Activity getDestination();
}
在pvm中,也包括了對event的支持,event的接口如下.
public interface EventListener extends Serializable {
void notify(EventListenerExecution execution) throws Exception;
}
ClientProcessDefinition definition = ProcessDefinitionBuilder.startProcess("jeffProcess")
.startActivity("initial", new AutomaticActivity())
.initial()
.transition("first")
.endActivity()
.startActivity("first", new WaitStateActivity())
.transition("end", "endSignal")
.endActivity()
.startActivity("end", new AutomaticActivity())
.endActivity()
.endProcess();
這里,我們將利用PVM所提供的Model,來實現一個基本的工作流引擎.
正如我們之前所說的,ActivityBehaviour是整個流程的定義核心所在,我們再看下它的API.
public interface ActivityBehaviour extends Serializable {
void execute(ActivityExecution execution) throws Exception;
}
public class Display implements ActivityBehaviour {
String message;
public Display(String message) {
this.message = message;
}
public void execute(ActivityExecution execution) {
System.out.println(message);
}
}
隱式(implicit)的execute方法執行順序
如果我們在execute()方法中,沒有顯示的調用execution中的方法,比如waitForSignal(),take(transition)等方法,那么默認的順序是這樣的.ClientProcessDefinition processDefinition = ProcessDefinitionBuilder.startProcess("helloworld")
.startActivity("a", new Display("Hello"))
.initial()
.transition("b")
.endActivity()
.startActivity("b", new Display("World"))
.endActivity()
.endProcess();
processDefinition.startProcessInstance();
Hello
World
public interface ExternalActivityBehaviour extends ActivityBehaviour {
//handles an external trigger.
void signal(ActivityExecution execution, String signalName, Map<String, ?> parameters) throws Exception;
}
我們接下來實現一個簡單的WaitState,實現ExternalActivityBehaviour的接口.
public class WaitState implements ExternalActivityBehaviour {
public void execute(ActivityExecution execution) {
execution.waitForSignal();
}
public void signal(ActivityExecution execution,
String signalName,
Map<String, Object> parameters) {
execution.take(signalName);
}
}
一樣的,我們來看一個簡單的從a->b的流程.這次不同的是,a和b都是wait state.
ClientProcessDefinition pd = ProcessDefinitionBuilder.startProcess("helloworld")
.startActivity("a", new WaitState())
.initial()
.transition("b", "b-transition")
.endActivity()
.startActivity("b", new WaitState())
.endActivity()
.endProcess();
ClientProcessInstance instance = pd.startProcessInstance();
instance.isActive("a")
在啟動之后,因為執行到a的時候,是一個wait state,所以,當前的流程活動應該是指向a. 如果要到b這個activity,那么就需要調用
instance.signal("b-transition");
instance.isActive("b")
接下來,我們基于前面兩種節點的實現,來實現一個稍微比較正式的流程(loan process).
ClientProcessDefinition pd = ProcessDefinitionBuilder.startProcess("loanprocess")
.startActivity("submit loan request", new Display("submit a loan request"))
.initial()
.transition("evaluate", "evaluate-transition")
.endActivity()
.startActivity("evaluate", new WaitState())
.transition("wiremoney", "approve")
.transition("end", "reject")
.endActivity()
.startActivity("wiremoney", new Display("wire the money"))
.transition("archive")
.endActivity()
.startActivity("archive", new WaitState())
.transition("end", "done")
.endActivity()
.startActivity("end", new WaitState())
.endActivity()
.endProcess();
instance = pd.startProcessInstance();
現在呢, evaluate這個節點有兩條支路,一個是approve,指向wiremoney節點;另外一個是reject,直接走向end.
假設我們選擇approve這條支路.
instance.signal("approve");
那么,我們就走向了wiremoney這個節點,因為wiremoney是個Display節點,所以它顯示完后,默認的走向下一個節點,archive.
同樣的,因為archive節點是個wait state,所以需要再一次的signal,才能走到end這個節點.
instance.signal("done");
public interface EventListener extends Serializable {
void notify(EventListenerExecution execution) throws Exception;
}
具體的Event是由ObservableElement和EventName來構成的.
public interface EventListenerExecution extends OpenExecution {
void fire(String eventName, ObservableElement eventSource);
}
我們來實現一個簡單的EventListener, 叫PrintLn
public class PrintLn implements EventListener {
String message;
public PrintLn(String message) {
this.message = message;
}
public void notify(EventListenerExecution execution) throws Exception {
System.out.println(message);
}
}
我們看下是怎么來定義一個具備有Events的ProcessDefinition:
ClientProcessDefinition pd = ProcessDefinitionBuilder.startProcess("ab")
.startActivity("a", new Display("Testing Event"))
.initial()
.transition("b")
.startEvent(Event.END)
.listener(new PrintLn("leaving a"))
.listener(new PrintLn("second message while leaving a"))
.endEvent()
.startEvent(Event.TAKE)
.listener(new PrintLn("taking transition"))
.endEvent()
.endActivity()
.startActivity("b", new WaitState())
.startEvent(Event.START)
.listener(new PrintLn("entering b"))
.endEvent()
.endActivity()
.endProcess();