Java开源工作流OSWorkflow常见问题

    OSWorkflow是一个Java开源的工作流,使用Apache许可。OSWorkflow的最大特点是灵活,它所面向的使用对象是专业开发人员,如果最终用户想调整工作流,那是不太现实的。OSWorkflow的流程定义使用自己的xml格式,不遵守XPDL规范。
    在 http://www.opensymphony.com/osworkflow/下载OSWorkflow,本文使用的是2.8版本的。
    安装包里有一个工作流示例,把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来个表,看一下就能明白这些表之间的关系。
    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();
}
    这样系统就可以支持事务了,如果业务数据放在一个单独的数据库,可以再配一个数据源,用业务的数据源进行业务数据的操作,这样也可以很好的工作。
    如果能够显示工作的流程,以及当前正在进行什么操作,这会给工作流系统增色不少。在OSWorkflow自带的示例程序中就有这样的功能,能够显示出一个流程图,并以红色边框表示下一步需要进行的操作。分析源代码可以看出,流程是保存为一个图片,图片中的一个步骤表示为一个方框,同时用一个xml文件记录图片中各个方框的位置,如左上角x、y坐标、宽度、高度,在程序运行的时候,从工作流系统查询出当前需要执行的步骤,然后从xml文件中检索出步骤所在方框的位置,在上面显示一个红色边框的矩形。
    以图形化的方式显示流程,原理很简单,但在做的时候需要画一个图片,并且把图片中方框的位置信息写到一个xml文件中,还是挺麻烦的。不知道OSWorkflow安装包中的流程设计器为什么不能运行,从网上下载了一个osworkflow PNG制作及生成lyt.xml文件的程序就可以运行,在这个设计器里面可以以图形化的方式设计流程,并可以导出流程图片,并有定义位置的lyt文件,比自己计算位置方便多了。但用设计器设计流程不太好用,好的做法是自己写流程的xml文件,然后在设计器里导出流程图片和位置lyt文件。这样在程序运行时,红色方框可能和图片上的对不上,可以对viewlivegraph.jsp中的偏移量进行调整,最终的效果还可以。
 

流程效果图
    创建工作流实例:
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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值