jBPM如何自定義task
1 背景
jBPM中,task表示一段業務邏輯,比如發送郵件、查詢數據庫等。jBPM支持的task的總類如下圖所示。除此之外,jBPM支持用戶自定義task,官方的叫法有domain-specific task、custom work items、custom service node。
2 例子
2.1 創建wid文件
文件的路徑src/main/resources/META-INF/MyWorkDefinitions.wid。文件內容如下。
[
// the Notification work item
[
"name" : "Notification",
"parameters" : [
"Message" : new StringDataType(),
"From" : new StringDataType(),
"To" : new StringDataType(),
"Priority" : new StringDataType(),
],
"displayName" : "Notification",
"icon" : "icons/notification.gif"
]
]
wid的全稱是work itemdefinition,使用MVEL語言,定義了一個custom work item。
2.2 註冊wid文件
創建src/main/resources/META-INF/drools.rulebase.conf,文件內容如下。
drools.workDefinitions = MyWorkDefinitions.wid
2.3 使用custom work item
重啓eclipse之後,你會發現元件面板多了一個Notification元件,你可以按照其他元件的使用方法使用這個元件。
2.4 創建JAVA類:NotificationWorkItemHandler
NotificationWorkItemHandler是一個必須實現接口org.kie.runtime.instance.WorkItemHandler的JAVA類,用於執行具體的業務邏輯。
import org.kie.api.runtime.process.WorkItem;
import org.kie.api.runtime.process.WorkItemHandler;
import org.kie.api.runtime.process.WorkItemManager;
public class NotificationWorkItemHandler implements WorkItemHandler {
public void executeWorkItem(WorkItem workItem, WorkItemManager manager) {
//獲取參數
String from = (String) workItem.getParameter("From");
String to = (String) workItem.getParameter("To");
String message = (String) workItem.getParameter("Message");
String priority = (String) workItem.getParameter("Priority");
//業務邏輯
EmailService service = ServiceRegistry.getInstance().getEmailService();
service.sendEmail(from, to, "Notification", message);
// 通知work item manager work item已經處理完成
manager.completeWorkItem(workItem.getId(), rm);
}
public void abortWorkItem(WorkItem workItem, WorkItemManager manager) {
System.out.println("Aborting");
}
}
2.5 關聯custom work item和WorkItemHandler
public class ServiceTaskExample {
public static void main(String[] args) {
KieServices ks = KieServices.Factory.get();
KieContainer kContainer = ks.getKieClasspathContainer();
KieBase kbase = kContainer.getKieBase("kbase");
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("category", "big");
variables.put("dollars", 100000);
RuntimeManager manager = createRuntimeManager(kbase);
RuntimeEngine engine = manager.getRuntimeEngine(null);
KieSession ksession = engine.getKieSession();
ksession.getWorkItemManager().registerWorkItemHandler("Notification", new NotificationWorkItemHandler());
ksession.startProcess("test5.test5",variables);
manager.disposeRuntimeEngine(engine);
System.exit(0);
}
private static RuntimeManager createRuntimeManager(KieBase kbase) {
JBPMHelper.startH2Server();
JBPMHelper.setupDataSource();
EntityManagerFactory emf = Persistence.createEntityManagerFactory("org.jbpm.persistence.jpa");
RuntimeEnvironmentBuilder builder = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder().entityManagerFactory(emf)
.knowledgeBase(kbase);
return RuntimeManagerFactory.Factory.get()
.newSingletonRuntimeManager(builder.get(), "com.sample:example:1.0");
}
}
完整的代碼如上,其中核心代碼如下:
ksession.getWorkItemManager().registerWorkItemHandler("Notification",new NotificationWorkItemHandler());
第一個參數"Notification"對應wid文件中的name,第二個參數是待註冊的workitem handler。
3 注意點
3.1 customwork item和workitem handler的界限
從前面的例子可以看出,我們需要結合custom work item和work item handler兩者來使用,兩者的區別是如下。
custom work item 側重於說明workitem是什麼,不會說明如何去實現,是一個高層次的描述。custom work item handler側重於說明如何來實現custom workitem的功能,有詳細的業務邏輯(體現在方法executeWorkItem中)。
3.2 自定義任務和BPMN2.0的關係
自定義的任務對應到BPMN2.0中的元件是”tak”,在上面的例子中的custom work item 對應的BPMN2.0的XML代碼如下。
<bpmn2:task id="Task_2" tns:taskName="Notification" tns:displayName="Notification" tns:icon="icons/notification.gif” name="Notification">
<bpmn2:extensionElements>
<tns:metaData name="elementname">
<tns:metaValue><![CDATA[Notification]]></tns:metaValue>
</tns:metaData>
</bpmn2:extensionElements>
<bpmn2:ioSpecification id="InputOutputSpecification_2">
<bpmn2:dataInput id="DataInput_2" itemSubjectRef="_2-2-4_InMessageType" name="Message"/>
<bpmn2:dataInput id="DataInput_3" itemSubjectRef="_2-2-4_InMessageType" name="From"/>
<bpmn2:dataInput id="DataInput_4" itemSubjectRef="_2-2-4_InMessageType" name="To"/>
<bpmn2:dataInput id="DataInput_5" itemSubjectRef="_2-2-4_InMessageType" name="Priority"/>
<bpmn2:inputSet id="_InputSet_2">
<bpmn2:dataInputRefs>DataInput_2</bpmn2:dataInputRefs>
<bpmn2:dataInputRefs>DataInput_3</bpmn2:dataInputRefs>
<bpmn2:dataInputRefs>DataInput_4</bpmn2:dataInputRefs>
<bpmn2:dataInputRefs>DataInput_5</bpmn2:dataInputRefs>
</bpmn2:inputSet>
<bpmn2:outputSet id="OutputSet_2" name="Output Set 2"/>
</bpmn2:ioSpecification>
<bpmn2:dataInputAssociation id="_DataInputAssociation_2">
<bpmn2:targetRef>DataInput_2</bpmn2:targetRef>
</bpmn2:dataInputAssociation>
<bpmn2:dataInputAssociation id="_DataInputAssociation_3">
<bpmn2:targetRef>DataInput_3</bpmn2:targetRef>
</bpmn2:dataInputAssociation>
<bpmn2:dataInputAssociation id="_DataInputAssociation_4">
<bpmn2:targetRef>DataInput_4</bpmn2:targetRef>
</bpmn2:dataInputAssociation>
<bpmn2:dataInputAssociation id="_DataInputAssociation_5">
<bpmn2:targetRef>DataInput_5</bpmn2:targetRef>
</bpmn2:dataInputAssociation>
</bpmn2:task>
3.3 workitem handler的生命週期的管理
如果handler的創建是非常輕量的,我們可以讓handler實現接口org.kie.internal.runtime.Closeable,實現這個接口後,handler的實例會隨着handlerowner即work item handler manager的關閉而釋放。
如果handler的創建是重量級的,比如加載了許多數據庫的數據,連接外部socket等等,我們可以通過實現接口org.kie.internal.runtime.Cacheable,來達到重用handler實例的目的。