osworkflow的使用

来源:互联网 发布:工程预算定额软件 编辑:程序博客网 时间:2024/05/22 13:15
OSWorkflow是一个Java开源的工作流,使用Apache许可。OSWorkflow的最大特点是灵活,它所面向的使用对象是专业开发人员,如果最终用户想调整工作流,那是不太现实的。OSWorkflow的流程定义使用自己的xml格式,不遵守XPDL规范。
    在http://www.opensymphony.com/osworkflow/下载OSWorkflow,本文使用的是2.8版本的。
1持久化
    安装包里有一个工作流示例,把osworkflow-2.8.0-example.war放到tomcat的webapps下面直接就可以看到效果了。但示例程序并没有对数据进行持久化,重启tomcat后数据就丢失了。OSWorkflow支持多种主流数据库,以下以mysql数据库为例,讲述如何把OSWorkflow的示例程序产生的数据保存到mysql数据库。
    1、在mysql数据库中执行建表脚本。
    在mysql数据库中建立一个数据库,如osworkflow,执行下载包中的src/etc/deployment/jdbc/mysql.sql,建立OSWorkflow所需要的数据库表。
    2、在应用的context中建立数据源。
    把mysql的jdbc驱动包放到tomcat的lib下面,建立一个数据源,代码如下:
<Resource name="jdbc/osworkflow" 
auth="Container" 
type="javax.sql.DataSource" 
driverClassName="com.mysql.jdbc.Driver" 
url="jdbc:mysql://localhost:3306/osworkflow" 
username="root" 
password="" 
maxActive="10" 
maxIdle="5" 
maxWait="10000" />
    3、修改WEB-INF/classes下的配置文件。
    修改osuser.xml文件,修改后的文件内容如下:
<opensymphony-user> 

    <!-- 
    <provider class="com.opensymphony.user.provider.memory.MemoryAccessProvider" /> 
    <provider class="com.opensymphony.user.provider.memory.MemoryCredentialsProvider" /> 
    <provider class="com.opensymphony.user.provider.memory.MemoryProfileProvider" /> 
    --> 
     
       <provider class="com.opensymphony.user.provider.jdbc.JDBCAccessProvider"> 
         <property name="user.table">os_user</property> 
         <property name="group.table">os_group</property> 
         <property name="membership.table">os_membership</property> 
         <property name="user.name" >username</property> 
         <property name="user.password">passwordhash</property> 
         <property name="group.name">groupname</property> 
         <property name="membership.userName" >username</property> 
         <property name="membership.groupName">groupname</property> 
         <property name="datasource">jdbc/osworkflow</property> 
       </provider> 
       <provider class="com.opensymphony.user.provider.jdbc.JDBCCredentialsProvider"> 
         <property name="user.table">os_user</property> 
         <property name="group.table">os_group</property> 
         <property name="membership.table">os_membership</property> 
         <property name="user.name" >username</property> 
         <property name="user.password">passwordhash</property> 
         <property name="group.name">groupname</property> 
         <property name="membership.userName" >username</property> 
         <property name="membership.groupName">groupname</property> 
         <property name="datasource">jdbc/osworkflow</property> 
       </provider> 
       <provider class="com.opensymphony.user.provider.jdbc.JDBCProfileProvider"> 
         <property name="user.table">os_user</property> 
         <property name="group.table">os_group</property> 
         <property name="membership.table">os_membership</property> 
         <property name="user.name" >username</property> 
         <property name="user.password">passwordhash</property> 
         <property name="group.name">groupname</property> 
         <property name="membership.userName" >username</property> 
         <property name="membership.groupName">groupname</property> 
         <property name="datasource">jdbc/osworkflow</property> 
       </provider> 
     
     
    <!-- 
        Authenticators can take properties just like providers. 

        This smart authenticator should work for 'most' cases - it dynamically looks up 
        the most appropriate authenticator for the current server. 
    --> 
    <authenticator class="com.opensymphony.user.authenticator.SmartAuthenticator" /> 
</opensymphony-user>
    修改osworkflow.xml文件,修改后的文件内容如下:
<osworkflow> 
    <!--<persistence class="com.opensymphony.workflow.spi.memory.MemoryWorkflowStore"/>--> 
     
  <persistence class="com.opensymphony.workflow.spi.jdbc.JDBCWorkflowStore"> 
    <property key="datasource" value="jdbc/osworkflow"/> 

      <property key="entry.sequence" value="select count(*) + 1 from os_wfentry"/>    
    <property key="entry.table" value="OS_WFENTRY"/> 
    <property key="entry.id" value="ID"/> 
    <property key="entry.name" value="NAME"/> 
    <property key="entry.state" value="STATE"/> 
    <property key="step.sequence" value="select sum(c1) + 1 from ( 
            select 1 as tb,  count(*)  as c1  
            from os_currentstep union  
            select 2 as tb, count(*) as c1 from os_historystep) as TabelaFinal"/> 
    <property key="history.table" value="OS_HISTORYSTEP"/> 
    <property key="current.table" value="OS_CURRENTSTEP"/> 
    <property key="historyPrev.table" value="OS_HISTORYSTEP_PREV"/> 
    <property key="currentPrev.table" value="OS_CURRENTSTEP_PREV"/> 
    <property key="step.id" value="ID"/> 
    <property key="step.entryId" value="ENTRY_ID"/> 
    <property key="step.stepId" value="STEP_ID"/> 
    <property key="step.actionId" value="ACTION_ID"/> 
    <property key="step.owner" value="OWNER"/> 
    <property key="step.caller" value="CALLER"/> 
    <property key="step.startDate" value="START_DATE"/> 
    <property key="step.finishDate" value="FINISH_DATE"/> 
    <property key="step.dueDate" value="DUE_DATE"/> 
    <property key="step.status" value="STATUS"/> 
    <property key="step.previousId" value="PREVIOUS_ID"/> 
  </persistence> 

    <factory class="com.opensymphony.workflow.loader.XMLWorkflowFactory"> 
        <property key="resource" value="workflows.xml" /> 
    </factory> 
</osworkflow>
    增加文件propertyset.xml,内容如下:
<propertysets> 
    <propertyset name="aggregate" class="com.opensymphony.module.propertyset.aggregate.AggregatePropertySet"/>
    <propertyset name="cached" class="com.opensymphony.module.propertyset.cached.CachingPropertySet"/> 
    <propertyset name="jdbc" class="com.opensymphony.module.propertyset.database.JDBCPropertySet"> 
        <arg name="datasource" value="jdbc/osworkflow"/> 
         
        <arg name="table.name" value="OS_PROPERTYENTRY"/> 
        <arg name="col.globalKey" value="GLOBAL_KEY"/> 
        <arg name="col.itemKey" value="ITEM_KEY"/> 
        <arg name="col.itemType" value="ITEM_TYPE"/> 
        <arg name="col.string" value="STRING_VALUE"/> 
        <arg name="col.date" value="DATE_VALUE"/> 
        <arg name="col.data" value="DATA_VALUE"/> 
        <arg name="col.float" value="FLOAT_VALUE"/> 
        <arg name="col.number" value="NUMBER_VALUE"/> 
    </propertyset> 
</propertysets>
    运行tomcat,对示例程序进行操作,就可以在mysql中看到数据了。OSWorkflow比较简单,也就用了10来个表,看一下就能明白这些表之间的关系。
2全局事务
    OSWorkflow中的示例程序使用BasicWorkflow,从文档上看这种方式不支持事务,这在实际应用时没有太大用处。但查看BasicWorkflow的源代码发现,这个类只是获取数据源,然后操作数据库,因为数据库连接默认的是自动提交的,这就造成了BasicWorkflow好像是不支持事务的。如果要实现工作流的事务,可以定义一个BasicWorkflow的子类来实现。也可以通过JOTM等分布式事务组件来实现事务,这种方式直接使用BasicWorkflow就可以。
    以下介绍OSWorkflow和JOTM集成的方法,实现事务功能。
    1、把需要的jar放到应用的lib下面。
    需要的jar包:howl.jar、jotm.jar、objectweb-datasource.jar、ow_carol.jar、xapool.jar。
    2、在应用的context中建立数据源。
    建立UserTransaction和数据源,代码如下:
<Resource name="UserTransaction" 
auth="Container" 
type="javax.transaction.UserTransaction" 
factory="org.objectweb.jotm.UserTransactionFactory" 
jotm.timeout="180" /> 

<Resource name="jdbc/osworkflow" type="javax.sql.DataSource" 
factory="org.objectweb.jndi.DataSourceFactory" 
maxWait="5000" 
maxActive="300" 
maxIdle="2" 
username="root" 
password="" 
driverClassName="com.mysql.jdbc.Driver" 
url="jdbc:mysql://127.0.0.1:3306/osworkflow" 
/>
    3、对于需要持久化的工作流操作,在代码中置于事务之下。
    代码示例如下:
UserTransaction ut =DBUtil.getUserTransaction(); 
Connection conn = DBUtil.getConn(); 
boolean rollback = false; 
try { 
  ut.begin(); 

  //*********业务部分 

  //*********工作流部分 
  //新建工作流实例 
  Workflow wf = new BasicWorkflow(employeeName); 
  long wfid = wf.initialize("请假流程", 0, null); 

  //执行动作 
  wf.doAction(wfid, 8, null); 

catch (Exception e) { 
  rollback = true; 
  e.printStackTrace(); 
  throw e; 

finally { 
  if (ut != null && ut.getStatus() != Status.STATUS_NO_TRANSACTION) { 
    if (rollback) ut.rollback(); 
    else ut.commit(); 
  } 

  if (conn != null) conn.close(); 
}
    这样系统就可以支持事务了,如果业务数据放在一个单独的数据库,可以再配一个数据源,用业务的数据源进行业务数据的操作,这样也可以很好的工作。
3流程的图形化显示
    如果能够显示工作的流程,以及当前正在进行什么操作,这会给工作流系统增色不少。在OSWorkflow自带的示例程序中就有这样的功能,能够显示出一个流程图,并以红色边框表示下一步需要进行的操作。分析源代码可以看出,流程是保存为一个图片,图片中的一个步骤表示为一个方框,同时用一个xml文件记录图片中各个方框的位置,如左上角x、y坐标、宽度、高度,在程序运行的时候,从工作流系统查询出当前需要执行的步骤,然后从xml文件中检索出步骤所在方框的位置,在上面显示一个红色边框的矩形。
    以图形化的方式显示流程,原理很简单,但在做的时候需要画一个图片,并且把图片中方框的位置信息写到一个xml文件中,还是挺麻烦的。不知道OSWorkflow安装包中的流程设计器为什么不能运行,从网上下载了一个osworkflow PNG制作及生成lyt.xml文件的程序就可以运行,在这个设计器里面可以以图形化的方式设计流程,并可以导出流程图片,并有定义位置的lyt文件,比自己计算位置方便多了。但用设计器设计流程不太好用,好的做法是自己写流程的xml文件,然后在设计器里导出流程图片和位置lyt文件。这样在程序运行时,红色方框可能和图片上的对不上,可以对viewlivegraph.jsp中的偏移量进行调整,最终的效果还可以。
 

流程效果图
4常用的操作
    创建工作流实例:
Workflow wf = new BasicWorkflow((String) session.getAttribute("username")); 
long wfid = wf.initialize("请假流程", 0, null);
    设置、获取属性:
Workflow wf = new BasicWorkflow((String) session.getAttribute("username")); 
long wfId = Long.parseLong(request.getParameter("wfId")); 
wf.getPropertySet(wfId).setString("biz_id", id); 
String bizId = wf.getPropertySet(wfId).getString("biz_id");
    执行动作:
Workflow wf = new BasicWorkflow((String) session.getAttribute("username")); 
long wfId = Long.parseLong(request.getParameter("wfId")); 
int actionId = Integer.parseInt(request.getParameter("actionId")); 
wf.doAction(wfId, actionId, null);
    获取工作列表:
List<Map> workList = new ArrayList<Map>(); 
Workflow wf = new BasicWorkflow((String) session.getAttribute("username")); 
//创建查询条件 
FieldExpression statusExp = new FieldExpression(FieldExpression.STATUS, 
    FieldExpression.CURRENT_STEPS, FieldExpression.EQUALS, "Underway"); 
FieldExpression ownerExp = new FieldExpression(FieldExpression.OWNER, 
    FieldExpression.CURRENT_STEPS, FieldExpression.EQUALS, employeeName); 
FieldExpression wfExp = new FieldExpression(FieldExpression.NAME, 
    FieldExpression.ENTRY, FieldExpression.EQUALS, "请假流程"); 
FieldExpression stepExp = new FieldExpression(FieldExpression.STEP, 
    FieldExpression.CURRENT_STEPS, FieldExpression.NOT_EQUALS, 2); 
NestedExpression exp = new NestedExpression(new Expression[]{statusExp, ownerExp, wfExp, stepExp}, 
    NestedExpression.AND); 
WorkflowExpressionQuery expQuery = new WorkflowExpressionQuery(exp); 
//查询 
List workflows = wf.query(expQuery); 
//获取工作流实例相关信息 
for (Iterator iterator = workflows.iterator(); iterator.hasNext();) { 
    Long wfId = (Long) iterator.next(); 
    Map item = new HashMap(); 
    item.put("wfId", wfId); 
    item.put("wfName", wf.getWorkflowName(wfId)); 
    item.put("biz_id", wf.getPropertySet(wfId).getString("biz_id")); 
    WorkflowDescriptor wd = wf.getWorkflowDescriptor(wf.getWorkflowName(wfId)); 
    int[] actionIds = wf.getAvailableActions(wfId, null); 
    List<Map> actions = new ArrayList<Map>(); 
    for (int i=0; i<actionIds.length; i++) { 
        Map action = new HashMap(); 
        action.put("actionId", actionIds[i]); 
        action.put("actionName", wd.getAction(actionIds[i]).getName()); 
        actions.add(action); 
    } 
    item.put("actions", actions); 
    workList.add(item); 
}
    改变工作流实例状态:
Workflow wf = new BasicWorkflow((String) session.getAttribute("username")); 
long wfId = Long.parseLong(request.getParameter("wfId")); 
wf.changeEntryState(wfId, WorkflowEntry.COMPLETED);
    如果执行到某个动作整个流程都结束的时候,可以用上面的语句,如果在工作流定义的xml中设置action属性 finish="true" ,则工作流实例会自动关闭。
0 0
原创粉丝点击