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实例的目的。