update:这个例子是不当的,更合适的例子请自己上javaeye上搜索个帖子。源码俺早就丢了,请不要再发邮件给我,谢谢。
osworkflow扩展非常容易,跟我们的应用结合起来使用也很容易。假设一个请假流程:员工请假,需要经过部门经理和人力资源部经理两人共同审批,只有当两人都许可时才通过,任一人驳回就失效,也就是一个and split和and Join流程,并且我们附加一个要求,当发送请假请求、许可和驳回这几个操作时都将发送一条消息给相应的用户。
流程定义文件如下:
请注意,我们在许可或者通过的时候 propertySet.setString("action2",......), propertySet.setString("action3",......) ,然后在join点判断,如果两个都是success,流程结束;如果一个是fail,就发送一个消息给员工。
发送消息的function像这样:
得到两个参数groupName和content(消息内容),调用业务对象发送消息。
完整代码下载在 《LeaveSystem》
代码用到了自己过去写的一个MVC框架和持久层,对此有兴趣的参考这三篇文章:
《设计自己的MVC框架》
《设计模式之事务处理》
《使用Annotation设计持久层》
如果仅仅是想了解osworkflow的应用,建议您跑下流程,读读相关几个业务类(LeaveServiceImpl.java,SendRemindInfFunction.java,service包下)即可。解压缩后的文件可以直接导入myeclipse工程,部署在tomcat下,数据库用的是oracle。跑起来以后可以用3个用户登录,test是雇员组,dennis是部门经理组,jordan是公司经理,都不需要密码。写的比较简单,只是实验性质,见谅。
我认为使用osworkflow,只要了解了它的表结构和主要原理,根据你的业务需要结合几张主要表(os_wfentry,os_currentstep,os_historystep等)合理设计数据库和业务流程,可以省去过去为每个业务流程对象创建的一大堆flag(标志,目前的流程状态)的累赘,充分利用工作流的威力。比如为部门经理和人力资源部经理显示不同的需要审批的假单列表,只要结合os_historystep表进行联合查询,部门经理的应该是执行了未执行acion2,step在3的;而人力资源部经理得到的同样是step在3,action未执行3的。
手痒痒,很想把去年为一家公司写的绩效考核系统改写一下,当时设计的一个contract对象拥有7,8个flag来标志合约状态(直接上级审核,人力资源评价,KPI评价等),搞的非常混乱,而且流程写死在代码里,如果以后要改变考核流程,只有重新写过一套。不过那家公司是国有企业,每年的固定的预算费用一定要花掉,反正大家一起赚国家的钱嘛。
osworkflow扩展非常容易,跟我们的应用结合起来使用也很容易。假设一个请假流程:员工请假,需要经过部门经理和人力资源部经理两人共同审批,只有当两人都许可时才通过,任一人驳回就失效,也就是一个and split和and Join流程,并且我们附加一个要求,当发送请假请求、许可和驳回这几个操作时都将发送一条消息给相应的用户。
流程定义文件如下:
<?
xml version="1.0" encoding="UTF-8"
?>
<! DOCTYPE workflow PUBLIC "-//OpenSymphony Group//DTD OSWorkflow 2.7//EN"
"http://www.opensymphony.com/osworkflow/workflow_2_7.dtd" >
< workflow >
< initial-actions >
< action id ="0" name ="开始" >
< pre-functions >
< function type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.Caller
</ arg >
</ function >
</ pre-functions >
< results >
< unconditional-result old-status ="Finished"
status ="Underway" step ="1" owner ="${caller}" />
</ results >
</ action >
</ initial-actions >
< steps >
< step id ="1" name ="填假单" >
< external-permissions >
< permission name ="permA" >
< restrict-to >
< conditions type ="AND" >
< condition type ="class" > <!-- 流程处于Underway状态(流程已经启动) -->
< arg name ="class.name" >
com.opensymphony.workflow.util.StatusCondition
</ arg >
< arg name ="status" > Underway </ arg >
</ condition >
< condition type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.AllowOwnerOnlyCondition
</ arg >
</ condition >
</ conditions >
</ restrict-to >
</ permission >
</ external-permissions >
< actions >
< action id ="1" name ="送出" >
< restrict-to >
< conditions type ="AND" >
< condition type ="class" > <!-- 流程处于Underway状态(流程已经启动) -->
< arg name ="class.name" >
com.opensymphony.workflow.util.StatusCondition
</ arg >
< arg name ="status" > Underway </ arg >
</ condition >
< condition type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.AllowOwnerOnlyCondition
</ arg >
</ condition >
</ conditions >
</ restrict-to >
< pre-functions >
< function type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.Caller
</ arg >
</ function >
</ pre-functions >
< results >
< unconditional-result old-status ="Finished"
split ="1" status ="Queued" >
< post-functions >
< function type ="class" >
< arg name ="class.name" >
net.rubyeye.leavesys.service.workflow.SendRemindInfFunction
</ arg >
< arg name ="groupName" >
AND (GROUPNAME='dept_manager' or
GROUPNAME='comp_manager')
</ arg >
< arg name ="content" >
you have leavemsg to
check!please check it!
</ arg >
</ function >
</ post-functions >
</ unconditional-result >
</ results >
</ action >
</ actions >
</ step >
< step id ="2" name ="部门经理批假单" >
< actions >
< action id ="2" name ="准许" >
< restrict-to >
< conditions >
< condition type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.OSUserGroupCondition
</ arg >
< arg name ="group" > dept_manager </ arg >
</ condition >
</ conditions >
</ restrict-to >
< pre-functions >
< function type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.Caller
</ arg >
</ function >
< function type ="beanshell" >
< arg name ="script" >
propertySet.setString("action1",
"success");
</ arg >
</ function >
</ pre-functions >
< results >
< unconditional-result old-status ="Finished"
status ="Queued" join ="1" owner ="${caller}" />
</ results >
</ action >
< action id ="3" name ="驳回" >
< restrict-to >
< conditions >
< condition type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.OSUserGroupCondition
</ arg >
< arg name ="group" > dept_manager </ arg >
</ condition >
</ conditions >
</ restrict-to >
< pre-functions >
< function type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.Caller
</ arg >
</ function >
< function type ="beanshell" >
< arg name ="script" >
propertySet.setString("action1",
"fail");
</ arg >
</ function >
</ pre-functions >
< results >
< unconditional-result old-status ="Finished"
status ="Queued" join ="2" owner ="${caller}" />
</ results >
</ action >
</ actions >
</ step >
< step id ="3" name ="公司经理批假单" >
< actions >
< action id ="4" name ="准许" >
< restrict-to >
< conditions >
< condition type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.OSUserGroupCondition
</ arg >
< arg name ="group" > comp_manager </ arg >
</ condition >
</ conditions >
</ restrict-to >
< pre-functions >
< function type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.Caller
</ arg >
</ function >
< function type ="beanshell" >
< arg name ="script" >
propertySet.setString("action2",
"success");
</ arg >
</ function >
</ pre-functions >
< results >
< unconditional-result old-status ="Finished"
status ="Queued" join ="1" owner ="${caller}" />
</ results >
</ action >
< action id ="5" name ="驳回" >
< restrict-to >
< conditions >
< condition type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.OSUserGroupCondition
</ arg >
< arg name ="group" > dept_manager </ arg >
</ condition >
</ conditions >
</ restrict-to >
< pre-functions >
< function type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.Caller
</ arg >
</ function >
< function type ="beanshell" >
< arg name ="script" >
propertySet.setString("action2",
"fail");
</ arg >
</ function >
</ pre-functions >
< results >
< unconditional-result old-status ="Finished"
status ="Queued" join ="2" owner ="${caller}" />
</ results >
</ action >
</ actions >
</ step >
< step id ="4" name ="停止" />
</ steps >
< splits >
< split id ="1" >
< unconditional-result old-status ="Finished" status ="Queued"
step ="2" />
< unconditional-result old-status ="Finished" status ="Queued"
step ="3" />
</ split >
</ splits >
< joins >
< join id ="1" >
< conditions type ="AND" >
< condition type ="beanshell" >
< arg name ="script" >
<![CDATA[
"Finished".equals(jn.getStep(2).getStatus()) &&
"Finished".equals(jn.getStep(3).getStatus())&&"success".equals(propertySet.getString("action1"))&&
"success".equals(propertySet.getString("action2"))
]]>
</ arg >
</ condition >
</ conditions >
< unconditional-result old-status ="Finished" status ="Queued"
step ="4" />
</ join >
< join id ="2" >
< conditions type ="OR" >
< condition type ="beanshell" >
< arg name ="script" >
<![CDATA[
"Finished".equals(jn.getStep(2).getStatus()) &&"fail".equals(propertySet.getString("action1"))
]]>
</ arg >
</ condition >
< condition type ="beanshell" >
< arg name ="script" >
<![CDATA[
"Finished".equals(jn.getStep(3).getStatus())&&"fail".equals(propertySet.getString("action2"))
]]>
</ arg >
</ condition >
</ conditions >
< unconditional-result old-status ="Finished" step ="4"
status ="Queued" >
< post-functions >
< function type ="class" >
< arg name ="class.name" >
net.rubyeye.leavesys.service.workflow.SendRemindInfFunction
</ arg >
< arg name ="groupName" >
AND GROUPNAME='employee'
</ arg >
< arg name ="content" >
you leveamsg is fail!!!
</ arg >
</ function >
</ post-functions >
</ unconditional-result >
</ join >
</ joins >
</ workflow >
<! DOCTYPE workflow PUBLIC "-//OpenSymphony Group//DTD OSWorkflow 2.7//EN"
"http://www.opensymphony.com/osworkflow/workflow_2_7.dtd" >
< workflow >
< initial-actions >
< action id ="0" name ="开始" >
< pre-functions >
< function type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.Caller
</ arg >
</ function >
</ pre-functions >
< results >
< unconditional-result old-status ="Finished"
status ="Underway" step ="1" owner ="${caller}" />
</ results >
</ action >
</ initial-actions >
< steps >
< step id ="1" name ="填假单" >
< external-permissions >
< permission name ="permA" >
< restrict-to >
< conditions type ="AND" >
< condition type ="class" > <!-- 流程处于Underway状态(流程已经启动) -->
< arg name ="class.name" >
com.opensymphony.workflow.util.StatusCondition
</ arg >
< arg name ="status" > Underway </ arg >
</ condition >
< condition type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.AllowOwnerOnlyCondition
</ arg >
</ condition >
</ conditions >
</ restrict-to >
</ permission >
</ external-permissions >
< actions >
< action id ="1" name ="送出" >
< restrict-to >
< conditions type ="AND" >
< condition type ="class" > <!-- 流程处于Underway状态(流程已经启动) -->
< arg name ="class.name" >
com.opensymphony.workflow.util.StatusCondition
</ arg >
< arg name ="status" > Underway </ arg >
</ condition >
< condition type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.AllowOwnerOnlyCondition
</ arg >
</ condition >
</ conditions >
</ restrict-to >
< pre-functions >
< function type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.Caller
</ arg >
</ function >
</ pre-functions >
< results >
< unconditional-result old-status ="Finished"
split ="1" status ="Queued" >
< post-functions >
< function type ="class" >
< arg name ="class.name" >
net.rubyeye.leavesys.service.workflow.SendRemindInfFunction
</ arg >
< arg name ="groupName" >
AND (GROUPNAME='dept_manager' or
GROUPNAME='comp_manager')
</ arg >
< arg name ="content" >
you have leavemsg to
check!please check it!
</ arg >
</ function >
</ post-functions >
</ unconditional-result >
</ results >
</ action >
</ actions >
</ step >
< step id ="2" name ="部门经理批假单" >
< actions >
< action id ="2" name ="准许" >
< restrict-to >
< conditions >
< condition type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.OSUserGroupCondition
</ arg >
< arg name ="group" > dept_manager </ arg >
</ condition >
</ conditions >
</ restrict-to >
< pre-functions >
< function type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.Caller
</ arg >
</ function >
< function type ="beanshell" >
< arg name ="script" >
propertySet.setString("action1",
"success");
</ arg >
</ function >
</ pre-functions >
< results >
< unconditional-result old-status ="Finished"
status ="Queued" join ="1" owner ="${caller}" />
</ results >
</ action >
< action id ="3" name ="驳回" >
< restrict-to >
< conditions >
< condition type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.OSUserGroupCondition
</ arg >
< arg name ="group" > dept_manager </ arg >
</ condition >
</ conditions >
</ restrict-to >
< pre-functions >
< function type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.Caller
</ arg >
</ function >
< function type ="beanshell" >
< arg name ="script" >
propertySet.setString("action1",
"fail");
</ arg >
</ function >
</ pre-functions >
< results >
< unconditional-result old-status ="Finished"
status ="Queued" join ="2" owner ="${caller}" />
</ results >
</ action >
</ actions >
</ step >
< step id ="3" name ="公司经理批假单" >
< actions >
< action id ="4" name ="准许" >
< restrict-to >
< conditions >
< condition type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.OSUserGroupCondition
</ arg >
< arg name ="group" > comp_manager </ arg >
</ condition >
</ conditions >
</ restrict-to >
< pre-functions >
< function type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.Caller
</ arg >
</ function >
< function type ="beanshell" >
< arg name ="script" >
propertySet.setString("action2",
"success");
</ arg >
</ function >
</ pre-functions >
< results >
< unconditional-result old-status ="Finished"
status ="Queued" join ="1" owner ="${caller}" />
</ results >
</ action >
< action id ="5" name ="驳回" >
< restrict-to >
< conditions >
< condition type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.OSUserGroupCondition
</ arg >
< arg name ="group" > dept_manager </ arg >
</ condition >
</ conditions >
</ restrict-to >
< pre-functions >
< function type ="class" >
< arg name ="class.name" >
com.opensymphony.workflow.util.Caller
</ arg >
</ function >
< function type ="beanshell" >
< arg name ="script" >
propertySet.setString("action2",
"fail");
</ arg >
</ function >
</ pre-functions >
< results >
< unconditional-result old-status ="Finished"
status ="Queued" join ="2" owner ="${caller}" />
</ results >
</ action >
</ actions >
</ step >
< step id ="4" name ="停止" />
</ steps >
< splits >
< split id ="1" >
< unconditional-result old-status ="Finished" status ="Queued"
step ="2" />
< unconditional-result old-status ="Finished" status ="Queued"
step ="3" />
</ split >
</ splits >
< joins >
< join id ="1" >
< conditions type ="AND" >
< condition type ="beanshell" >
< arg name ="script" >
<![CDATA[
"Finished".equals(jn.getStep(2).getStatus()) &&
"Finished".equals(jn.getStep(3).getStatus())&&"success".equals(propertySet.getString("action1"))&&
"success".equals(propertySet.getString("action2"))
]]>
</ arg >
</ condition >
</ conditions >
< unconditional-result old-status ="Finished" status ="Queued"
step ="4" />
</ join >
< join id ="2" >
< conditions type ="OR" >
< condition type ="beanshell" >
< arg name ="script" >
<![CDATA[
"Finished".equals(jn.getStep(2).getStatus()) &&"fail".equals(propertySet.getString("action1"))
]]>
</ arg >
</ condition >
< condition type ="beanshell" >
< arg name ="script" >
<![CDATA[
"Finished".equals(jn.getStep(3).getStatus())&&"fail".equals(propertySet.getString("action2"))
]]>
</ arg >
</ condition >
</ conditions >
< unconditional-result old-status ="Finished" step ="4"
status ="Queued" >
< post-functions >
< function type ="class" >
< arg name ="class.name" >
net.rubyeye.leavesys.service.workflow.SendRemindInfFunction
</ arg >
< arg name ="groupName" >
AND GROUPNAME='employee'
</ arg >
< arg name ="content" >
you leveamsg is fail!!!
</ arg >
</ function >
</ post-functions >
</ unconditional-result >
</ join >
</ joins >
</ workflow >
请注意,我们在许可或者通过的时候 propertySet.setString("action2",......), propertySet.setString("action3",......) ,然后在join点判断,如果两个都是success,流程结束;如果一个是fail,就发送一个消息给员工。
发送消息的function像这样:
package
net.rubyeye.leavesys.service.workflow;
import java.sql.SQLException;
import java.util.Map;
import net.rubyeye.leavesys.domain.RemindInf;
import net.rubyeye.leavesys.service.ManagerFactory;
import com.opensymphony.module.propertyset.PropertySet;
import com.opensymphony.workflow.FunctionProvider;
import com.opensymphony.workflow.WorkflowException;
public class SendRemindInfFunction implements FunctionProvider {
public void execute(Map transientVars, Map args, PropertySet ps)
throws WorkflowException {
String groupName = (String) args.get( " groupName " );
String content = (String) args.get( " content " );
RemindInf remindInf = new RemindInf();
remindInf.setContent(content);
try {
ManagerFactory.getRemindService().addRemindInfByGroupName(
groupName, remindInf);
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
import java.sql.SQLException;
import java.util.Map;
import net.rubyeye.leavesys.domain.RemindInf;
import net.rubyeye.leavesys.service.ManagerFactory;
import com.opensymphony.module.propertyset.PropertySet;
import com.opensymphony.workflow.FunctionProvider;
import com.opensymphony.workflow.WorkflowException;
public class SendRemindInfFunction implements FunctionProvider {
public void execute(Map transientVars, Map args, PropertySet ps)
throws WorkflowException {
String groupName = (String) args.get( " groupName " );
String content = (String) args.get( " content " );
RemindInf remindInf = new RemindInf();
remindInf.setContent(content);
try {
ManagerFactory.getRemindService().addRemindInfByGroupName(
groupName, remindInf);
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
得到两个参数groupName和content(消息内容),调用业务对象发送消息。
完整代码下载在 《LeaveSystem》
代码用到了自己过去写的一个MVC框架和持久层,对此有兴趣的参考这三篇文章:
《设计自己的MVC框架》
《设计模式之事务处理》
《使用Annotation设计持久层》
如果仅仅是想了解osworkflow的应用,建议您跑下流程,读读相关几个业务类(LeaveServiceImpl.java,SendRemindInfFunction.java,service包下)即可。解压缩后的文件可以直接导入myeclipse工程,部署在tomcat下,数据库用的是oracle。跑起来以后可以用3个用户登录,test是雇员组,dennis是部门经理组,jordan是公司经理,都不需要密码。写的比较简单,只是实验性质,见谅。
我认为使用osworkflow,只要了解了它的表结构和主要原理,根据你的业务需要结合几张主要表(os_wfentry,os_currentstep,os_historystep等)合理设计数据库和业务流程,可以省去过去为每个业务流程对象创建的一大堆flag(标志,目前的流程状态)的累赘,充分利用工作流的威力。比如为部门经理和人力资源部经理显示不同的需要审批的假单列表,只要结合os_historystep表进行联合查询,部门经理的应该是执行了未执行acion2,step在3的;而人力资源部经理得到的同样是step在3,action未执行3的。
手痒痒,很想把去年为一家公司写的绩效考核系统改写一下,当时设计的一个contract对象拥有7,8个flag来标志合约状态(直接上级审核,人力资源评价,KPI评价等),搞的非常混乱,而且流程写死在代码里,如果以后要改变考核流程,只有重新写过一套。不过那家公司是国有企业,每年的固定的预算费用一定要花掉,反正大家一起赚国家的钱嘛。