踩一踩OSWorkflow和jBMP:39行代码实现一个很好很强大的工作流引
? 现成的工作流引擎有很多,我以前曾浅尝过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,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",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,m-> o.state = 'assign' o.principals = [p.principal] } ],draft : [ Save : {o,m-> o.state = 'draft' o.principals = [o.originator] },assign : [ Submit : {o,m-> o.state = 'working' } ],working : [ Ask : {o,m-> o.state = 'has_questions' o.principals = [o.originator] },m-> o.state = 'report_ready' o.principals = [o.originator] } ],has_questions : [ Answer : {o,m-> o.state = 'working' o.principals = [o.previousPrincipal] } ],report_ready : [ OK : {o,m-> o.state = 'end' o.principals = [o.originator] },NG : {o,_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男 :-)? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |