
现成的工作流引擎有很多,我以前曾浅尝过OSWorkflow和jBPM,但都未能深入研究。总感觉它们过于复杂,术语也特别多,让我对它们逐渐失去了兴趣(还有那些流程设计器,个人觉得完全没有存在的必要:1.一般的用户用不来;2.程序员直接写代码(流程定义代码量一般几十行就够了),要流程设计器干嘛?)。另外有一个开源的基于Grails的工作流项目:http://www.grailsflow.org/ ,尽管它宣称“GrailsFlow: Your Workflow just got easier!”,我感觉它还是做得太复杂,凭我多年的IT从业经验,竟然没看懂,我也不想继续把时间浪费在它上面。
工作流引擎,从本质上说,是状态机,只要控制好了各个状态之间的转换就行了。我使用Groovy/Grails有一段时间了,感觉Grails的webflow设计得不错,简单易懂,很好很强大,只可惜它只适合做在线支付之类的流程。但是webflow却给了我一些启发,尤其是它那优美的DSL方式的流程定义,让我印象深刻。利用Groovy的BuilderSupport,Closure,还有我已经整合到Grails中的db4o持久化框架,很容易快速写出一个工作流引擎。
我也用一把DSL,一个流程定义的例子如下:
start {
on('Submit') {o->
println o.state
// change o.state to a new state ...
}
}
state1 {
on('Action11') {o->
// do something with o and then change o.state to a new state ...
}
}
state2 {
on('Action21') {o->
// do something with o and then change o.state to a new state ...
}
on('Action22') {o->
// do something with o and then change o.state to a new state ...
}
}
怎么样,够简洁吧?
在某个状态下,请求某个操作后,要执行的具体动作在对应的closure中写好了。这些closures中的代码犹如诸葛亮的锦囊妙计,工作流引擎依计行事。
现在设想我们把DSL写在workflows目录下的demo.groovy(扩展名随便你怎么写)中,怎样使工作流引擎读懂这个流程呢?这需要一点点Groovy Builder的知识,自己看Groovy官方文档或Google。
我们的FlowBuilder长得像这个样子:
class FlowBuilder extends BuilderSupport {
private Map flow = [:]
private String _state
protected void setParent(Object parent, Object child){}
protected Object createNode(Object name){
createNode(name, null)
}
protected Object createNode(Object name, Object value){
createNode(name, null, value)
}
protected Object createNode(Object name, Map attributes){
createNode(name, attributes, null)
}
protected Object createNode(Object name, Map attributes, Object value){}
protected void nodeCompleted(Object parent, Object node) {}
def invokeMethod(String name, args) {
switch(name) {
case 'flow':
super.invokeMethod name, args
break
case 'on':
flow[_state][args[0]] = args[1]
break
default:
_state = _state ? name : 'start' // the first state is always 'start'
flow[_state] = flow[_state] ?: [:]
super.invokeMethod name, args
break
}
}
}
简单测试一下:
def builder = new FlowBuilder()
def s = '{->'+'''
// DSL可从文件中读取
start {
on('Submit') {o->
println o.state
// change o.state to a new state ...
}
}
state1 {
on('Action11') {o->
// do something with o and then change o.state to a new state ...
}
}
state2 {
on('Action21') {o->
// do something with o and then change o.state to a new state ...
}
on('Action22') {o->
// do something with o and then change o.state to a new state ...
}
}
'''+'}'
this.class.classLoader.rootLoader.addURL( new URL("file:///E:/lib/bsf-2.4.0.jar") )
def closure = new org.apache.bsf.BSFManager().eval("groovy", null, 0, 0, s)
builder.flow closure
println builder.flow
输出以下内容:
[start:[submit:_$_run_closure1_closure2_closure5@31f2a7], state1:[action11:_$_run_closure1_closure3_closure6@131c89c], state2:[action21:_$_run_closure1_closure4_closure7@1697b67, action22:_$_run_closure1_closure4_closure8@24c4a3]]
这表明,流程定义已经被成功地装入一Map中。
其实,代码可以写得更精简:直接在文件中写成Map的形式(这可以看作另一种形式的DSL),以下是文章开头的图片所对应的工作流流程定义:
[
start : [
'Save as Draft' : {o, p, m->
o.state = 'draft'
o.principals = [m]
}
, Submit : {o, p, m->
o.state = 'assign'
o.principals = [p.principal]
}
]
, draft : [
Save : {o, p, m->
o.state = 'draft'
o.principals = [o.originator]
}
, Submit : {o, p, m->
o.state = 'assign'
o.principals = [p.principal]
}
]
, assign : [
Submit : {o, p, m->
o.state = 'working'
}
]
, working : [
Ask : {o, p, m->
o.state = 'has_questions'
o.principals = [o.originator]
}
, Submit : {o, p, m->
o.state = 'report_ready'
o.principals = [o.originator]
}
]
, has_questions : [
Answer : {o, p, m->
o.state = 'working'
o.principals = [o.previousPrincipal]
}
]
, report_ready : [
OK : {o, p, m->
o.state = 'end'
o.principals = [o.originator]
}
, NG : {o, p, m->
o.state = 'working'
o.principals = [o.previousPrincipal]
}
]
, _ds : [
report_type : {
return [['sas','SAS report'], ['cogonos','Cogonos report']]
}
, principal : {
def principals = []
User.findAll(sort:'username').each {
principals << [it.username, it.profile?.name ?: it.username]
}
principals
}
]
]
这样连FlowBuilder也省了,一行eval代码就解析并载入了流程定义。。。
以上内容还没涉及到我的工作流引擎,其实有了groovy语言的closure的强大支持,实现这个引擎实在是小菜一碟:
class WorkflowService {
static transactional = true
def formService
def process(params) {
def params_values = JSON.parse(params.values)
def workflowBean = WorkflowBean.find(id:params_values._bid)
def workflow
if(workflowBean) {
workflow = getWorkflow(workflowBean)
} else {
def workflowDef = WorkflowDefinition.find(id:params_values._fid)
if(workflowDef) {
workflow = getWorkflow(workflowDef)
workflowBean = new WorkflowBean(state:'start', definition:workflowDef)
}
}
def state = workflowBean.state
def action = workflow."$state"?."${params_values._action}"
if(action && action instanceof Closure) {
action.call(workflowBean, params_values, params.session.user.username)
workflowBean.save()
notifyPrincipals(workflowBean)
return [ok:true]
}
def arrayStore = [:]
workflow?._ds?.each {
arrayStore[it.key] = it.value.call()
}
def values = [_fid:workflowBean?.definition?.id, _bid:workflowBean?.id, _state:workflowBean?.state]
workflowBean?.data?.each {
values[it.key] = it.value
}
[workflowBean:workflowBean,
form:formService.getFlowForm(workflowBean, params.servletContext),
values:values as JSON,
arrayStore:arrayStore as JSON,
_actions:workflow?."${workflowBean?.state}"?.keySet() as JSON]
}
}
只有39行代码 -- 不能望39K女项背,我只能做个39L男 :-)
本文探讨了如何利用Groovy语言及其Builder特性构建一个简洁且易于理解的工作流引擎,并通过实例展示了如何用DSL进行流程定义与执行。通过GroovyBuilder简化流程解析,实现工作流引擎的核心功能,同时强调了Groovy语言在工作流场景下的优势与实践。
8019

被折叠的 条评论
为什么被折叠?



