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