jPDL和XUL结合

之前用jPDL时候有一个头疼的地方是界面的整合开发。jPDL designer可以生成jsf的界面(但因为对jsf的敬畏而没有去进一步考证),后来看到liferay里的一种做法是给任务节点上定义一些页面元素,比如 username:text, password:password, gender:checkbox(M,F)…记得不太清楚了,但是意思就是把一些关于界面的参数放到流程定义里,再通过工具来生成相应的页面。
思路比较清楚,也比较简单。

后来了解了一些XUL的东西,觉得似乎这些界面参数也可以用XUL来定义,相对标准化一点,而且也容易扩展。标准化的优点就是每个人都可以写自己的XUL解析和界面生成引擎,或者有统一的引擎可以用。

先YY记录下来。

seven forms of bpm – after read

seven forms of bpm – after read

use case 1:bpm as a discipline
refers to the analysis, documentation and improvement of the procedures that
describe how people and systems work together in an organization.
In the context of BPM as a discipline, we believe that a process model
from a non technical business analyst can never be translated into an executable process model by
just adding technical details to it.
Who is going to spend the time to link the
analysis blocks to the executable blocks and then keep that mapping up to date.
jBPM’s jPDL language is designed to facilitate this approach. First of all, it’s based on free graph
modeling. Secondly, it has so called actions, which can be seen as event listeners.
This allows the developer to add programming logic without
changing the diagram structure.
Also super-states are often used in the context of creating better
communication between business analyst and developer.

Use Case 2 : Combining template based and ad hoc task management
But what
is often overlooked is that template based task orchestration only suits for a limited number of
scenarios.
First the process must be relatively stable. And secondly, enough executions of this
process have to happen so that the gain that can be achieved with software support is worth the
development effort.
In jBPM 4, the task management component will support this ad hoc human tasks. The
combination will be awsome. Human tasks in processes can be specified at a course grained level.

Use case 3 : Transactional asynchronous architectures
BPEL also focuses on asynchronous architectures, but then in a (web) services environment, rather
then a Java environment.
From the history
information it is very easy to collect valuable statistical information like the average time in each
step of the process.

Use case 4 : Service orchestration
Service orchestration is actually a variant of the previous use case ‘transactional asynchronous
architectures’.

Use Case 5 : Visual programming
With visual programming, we will target developers that do not yet have the full skillset to develop
in Java.
But visual
programming can lower the treshold to build applications for developers that have no or limited
Java knowledge.

Use Case 6 : Thread Control Language
We’ll develop a Thread Control Language which lets you specify a multithreaded Java concurrency
by drawing forks, joins and method activities.

Use Case 7 : Easy creation of DSLs
General purpose process languages are different from domain specific process languages.
There is even an easier way. Instead of creating a full process language for a specific purpose, it is
also possible to leverage jPDL’s capabilities and just add new node types to it.

[源码研究]jbpm中的异步Action

在jbpm中,如果action可以配置为异步执行

<action class=”ActionClassName” async=”true”></action>

如果是异步action,那么在flow执行到action所依附的节点时,会保存一个job,然后交由单独的job执行线程处理.源代

码:
GraphElement.java的executeActions方法:

if (action.isAsync()) {
ExecuteActionJob job = createAsyncActionExecutionJob(executionContext.getToken(), action);
MessageService messageService = (MessageService) Services.getCurrentService

(Services.SERVICENAME_MESSAGE);
messageService.send(job);
} else {
executeAction(action, executionContext);
}

这个job保存到数据库,并由JobExecutor去执行.

而jobExecutor在默认情况下是不会被启用的,需要手动启用或者配置为自动启动的servlet(JobExecutorServlet).
手动启动通过这个方法:

JbpmConfiguration.startJobExecutor()

还需要做的工作,在jbpm.cfg.xml里定义一个jobExecutor

<bean name=”jbpm.job.executor” class=”org.jbpm.job.executor.JobExecutor” singleton=”true” />

如果不用JbpmConfiguration应该也可以启动.做的事都是一样的,创建一个新的JobExecutor然后启动线程.只是猜测,没试过.

在3.1的数据库里是没有job的表的,升级到3.2.2后才有这个表.

使用springmodules启动子流程的问题

今天在整合jbpm时遇到这样一个错误can‘t create process instance when processdefinition is null
因为使用了subprocess,跟进源代码看了一下:(我用的是springmodules整合jbpm和spring)

发现子流程实例无法创建,因为没有context(JbpmContext).问题最终归结在怎样启动流程实例.原来的做法是:

ProcessDefinition pd = jbpmTemplate.getProcessDefinition();
ProcessInstance pi = new ProcessInstance(pd,variables);
pi.getRootToken().signal();

这样启动,如果没有子流程是没问题的.但是如果有了子流程,ProcessState(子流程state)会通过当前JbpmContext
去启动子流程.而上面这种做法是没有JbpmContext的!正确的做法是:

jbpmTemplate.execute(new JbpmCallback(){
public Object doInJbpm(JbpmContext context) throws JbpmException {
HashMap<String,Object> variables = new HashMap<String, Object>();
ProcessInstance pi = new ProcessInstance(jbpmTemplate.getProcessDefinition()
,variables);
pi.getRootToken().signal();
return null;
}
});

这种做法会初始化一个新的context然后启动流程.问题解决.但要注意设置subprocess的属性binding=”late”.如下:

<process-state name=”V3-sub-process” >
<sub-process name=”V3″ binding=”late”></sub-process>
<variable access=”read,write” name=”objType”></variable>
<variable access=”read,write” name=”objId”></variable>
<transition to=”tradeMatch”></transition>
</process-state>

意味着subprocess不是在初始化父流程时就初始化(findSubprocess),而是在processState.execute时候才初始化(这个时候才有context).如果不加binding=”late”,还是会有同样的错误.

另:在网上找解决方法时候发现,这个issue.貌似目前springmodules0.8不支持context外包含子流程的流程实例创建.所以做法应该是没错的.check过了,0.9也没有支持

jPDL的扩展思考

基于自己目前对jBPM的了解,发现使用jBPM做为工作流引擎时流程可以通过jPDL很方便的定义和发布。主要的工作来自于界面的定制,因为觉得 想要做一个通用的jBPM界面简直是不可能(或者是我目前的思路有问题),每个流程的流程meta是没问题,比方说把transition定义为一个 button然后去调用webcontroller,但是业务meta就不行了,某个字段可能需要显示为一个dropdown而不是文本框。尽管jPDL designer可以生成form但也只能生成比较简单的文本框和按钮,而且是基于jsf。

偶然中发现了liferay中对jPDL的扩展恰好是我想要的,增加了表单数据类型的定义

每个node的变量定义为:
text:username
textarea:introduction
checkbox:married:yes,no

“liferayworkflow portletjbpm的变量定义经行了扩展,可以看到每个变量的名字都由多部分组成其中用冒号分隔,workflow portlet会根据变量的前缀自动为该变量生成html中的表单。”

现在扩展是扩展了,但是如果想要加入业务规则呢?试试另一种思路:业务数据是在流在工作流上的,那么在不同的流程阶段(phase)要满足不同的业 务规则。这里就有一个问题了,规则是应该作为流程的一部分还是作为数据的一部分,显然应该是流程的一部分。但流程怎么能够知道这些meta(某个数据在某 个节点上要怎么显示并满足哪些规则)呢 ?(其实这些东西早就有现成的产品了。比如PEGA)这个时候就又需要扩展jPDL了。

考虑怎么去扩展jPDL:每个流程应该有一个flowBean的model,他可能包含很多其他的业务数据对象,这样每个流程所要操作的数据就都在 这个flowBean里,而规则和显示则要定义到流程的每个节点。有点类似JPF(java page flow,weblogic portal用的webmvc)。

先想到这里,以后再补充。

jBPM中task的signalling和blocking属性

今天发现在创建一个task实例之后,数据库中会出现两个task记录,不同的只是其中一个的issignaling是r,另一个是null.而且这两个使用的也是同一个token.查看jBPM的手册中的task management发现这样解释的:

A signalling task instance is a task instance that, when completed, can send a signal to its token to continue the process execution. Task instances can be blocking, meaning that the related token (=path of execution) is not allowed to leave the task-node before the task instance is completed. By default task instances are signalling and non-blocking.

信令和阻塞,当执行完成时发送一个signal给token..而blocking意味着token被阻塞在task-node直到task完成.不理解这个要在什么样的情况下使用,是不是分支任务的时候?

但是还是很奇怪,我这里只是 把start-state的task改成blocking=true就解决问题了.难道和state有关系吗?

有可能是我在生成processInstance时候有问题. 代码如下:

// create a new process instance to run
ProcessInstance processInstance = new ProcessInstance(processDefinition);
// create a new taskinstance for the start task
TaskInstance taskInstance = processInstance.getTaskMgmtInstance().createStartTaskInstance();
//signal
taskInstance.getToken().signal();
context.save(processInstance);

这样会生成两个taskinstance,而且一个是signaling一个是blocking??????

jBPM的性能测试

其实也还是hibernate得性能测试.不过为了满足boss的要求,还是写了一个测试.

启动20个线程,每个形成使用JbpmContext生成1000个ProcessInstance,然后遍历每个节点.

测试结果: 每个线程所需时间范围是360 – 420 s

用jconsole看了一下,瓶颈主要在网络读取(jdbc driver的socketread)和连接池等待(waiting available, checkout)上 .

import java.io.FileNotFoundException;
import java.io.IOException;

import org.jbpm.JbpmContext;
import org.jbpm.JbpmException;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.taskmgmt.exe.TaskInstance;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.util.Log4jConfigurer;
import org.springmodules.workflow.jbpm31.JbpmCallback;
import org.springmodules.workflow.jbpm31.JbpmTemplate;

import com.jpmchase.zpcclass.common.util.SpringUtils;
import com.jpmchase.zpcclass.workflow.service.WorkFlowService;

public class JbpmScalTest {
static {
SpringUtils.initFromClasspath(new String[]{”com/jpmchase/zpcclass/applicationContext*.xml”});
try {
Log4jConfigurer.initLogging(”classpath:com/jpmchase/zpcclass/log4j.properties”);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
WorkFlowService wfs;
private JbpmTemplate jbpmTemplate;
@Before
public void setUp() throws Exception {
wfs = (WorkFlowService) SpringUtils.getBean(”workFlowService”);
jbpmTemplate = (JbpmTemplate) SpringUtils.getBean(”jbpmTemplate”);
}

@After
public void tearDown() throws Exception {
}

public static void main(String[] args) throws Exception {
int threadcount = Integer.parseInt(args[0]);
int cyclecount = Integer.parseInt(args[1]);
String testtype = args[2];
JbpmScalTest test = new JbpmScalTest();
test.setUp();
//test.testScal2();
test.go(threadcount, cyclecount, testtype);
}
class FlowRunner implements Runnable{
String name,unit=”simple”;
long elapse,cycle;
public FlowRunner(String n,long c,String u){
name=n;cycle=c;unit=u;
}
public long getElapse(){return elapse;}
public void run(){
System.out.println(name+” started”);
long start = System.currentTimeMillis();
jbpmTemplate.execute(new JbpmCallback(){
public Object doInJbpm(JbpmContext context) throws JbpmException {
if(unit.equals(”simple”)){
simpleUnit(context);
}else{
fullUnit(context);
}
return null;
}
});
elapse = System.currentTimeMillis() – start;
System.out.println(name+”,”+elapse/1000+”,”+cycle+”,”+unit);
}
public void simpleUnit(JbpmContext context){
ProcessDefinition pd = (ProcessDefinition)context.getGraphSession().findLatestProcessDefinition(”trademgmt”);
for(int i=0;i<cycle;i++){
ProcessInstance pi = new ProcessInstance(pd);
pi.getTaskMgmtInstance().createStartTaskInstance();
}
}
public void fullUnit(JbpmContext context){
ProcessDefinition pd = (ProcessDefinition)context.getGraphSession().findLatestProcessDefinition(”trademgmt”);
for(int i=0;i<cycle;i++){
ProcessInstance pi = new ProcessInstance(pd);
TaskInstance ti = pi.getTaskMgmtInstance().createStartTaskInstance();

ti.getToken().signal(”saveInput”);
ti.getToken().signal(”acceptInput”);
ti.getToken().signal(”releaseTrade”);
ti.getToken().signal(”generateObligation”);
ti.getToken().signal(”generateAccountEntry”);
}
}

}
@Test
//2 threads
public void testScal1() throws IOException {
go(20,100,”full”);
go(20,100,”simple”);
System.in.read();
}

private void go(int threadcount,int cyclecount,String testtype)throws IOException {
FlowRunner fr;
Thread t;
for(int i = 0;i<threadcount;i++){
fr = new FlowRunner(”runner”+i,cyclecount,testtype);
t = new Thread(fr);
t.start();
}
}

@Test
//20 threads
public void testScal2() throws IOException {
go(20,1000,”full”);
go(20,1000,”simple”);
System.in.read();
}
}

jBPM中的assignment

ZhangV

在jbpm的流程定义文件(processdefinition.xml)中当定义一个task的assignment的时候有几种选择:

  • Swimlane
  • Pooled Actors
  • Expression
  • Actor
  • Handler

这里分别描述一下每种的用法,以及使用场景。使用场景的理解很重要,只有清楚每一种用法的所适合的情形,以及他们之间的相互转换才能说是真的“会用了”。

Swimlane
泳道是用来处理一个流程中的多个任务必须由同一个人来处理的机制。所以,当一个流程的第一个任务实例被创建到指定的泳道后,第一个任务实例中的执行人被保 存,该泳道中的后续任务的处理人都是第一个任务的处理人。所以一个泳道有一个人员被分配,并且泳道中的任务不需要再分配人员。这里是指这个流程中的参与者或者角色。在实际的业务系统中,他可能根本就不是一个业务角色,可能是一个外部应用,也可能是一个处理过程,或者是几个业务角色的结合。所以要清楚他是指“在该流程中”的角色。在jPDL的user guide中写的很清楚也反复强调 – “process role”。
通过在流程定义文件中定义:
<swimlane name=”leader”>
<assignment class=”com.modofo.engine.swimlane.LeaderSwimlane”></assignment>
</swimlane>
同样可以实现运行时的结合实际应用进行灵活的任务分配。这也是目前我看到的使用比较多的方式。

Actor
相当于设置TaskInstance的actorId属性,主要用于获取某个actor的所有任务列表。设置方法在定义中描述actor-id属性,或者调用TaskInstance.setActorId(String)方法,或者在AssignmentHandler中调用assignable.setActorId(String)方法。

Pooled Actors
用于定义所有的任务参与人(candidates for task instance)。在查找某个参与者可参与的任务时使用,其实就是一个actor的集合,jBPM通过TaskMgmtSession.findPooledTaskInstances(String actorId)或者TaskMgmtSession.findPooledTaskInstances(List actorIds) 来查找任务集合。
注意:actor的优先级比pooled actors要高,所以如果同时设置两者,那jBPM只会用actor来查找,而忽略pooled actors。

一句话: actor被用来获取某个参与者的个人任务列表,pooled actors用来获取某个参与者可以参与的所有任务列表,或者说组任务列表。
使用这个actor集合的初衷就是为了不让任务分配和角色体系耦合。流程定义中始终都是只有参与者的名称和集合,而不存在用户,角色和用户组的具体设计。而使用Expression就耦合了这些信息在流程定义里。

Expression
如果你使用jBPM的identity component的话。你可以使用表达式定义的任务分配模式。通常这个表达式的形式是:

<assignment expression=’previous –> group(hierarchy) –> member(boss)’ />
我看了之后也不是很清楚,group和member是jBPM的identity componenet的概念,这个表达的意思大概是:使用之前的actor,然后在hierarchy组,并且member(角色)是boss的所有actor。这种方法仅限于使用jBPM的IC时使用。

Handler

指定你自己的AssignmentHandler实现。该接口只有assign方法。这种任务分配方式主要用于运行时的任务分配,而不是hardcode到定义文件里。使用场景,如果不想让原本应用中的访问控制与jBPM耦合起来,就可以使用这种方法来将jBPM中的任务分配代理出来。当然更好的定义是用swimlane,这样可以让定义文件看起来不是很凌乱。