每日一得

不求多得,只求一得 about java,hibernate,spring,design,database,Ror,ruby,快速开发
最近关心的内容:SSH,seam,flex,敏捷,TDD
本站的官方站点是:颠覆软件

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  220 随笔 :: 9 文章 :: 421 评论 :: 0 Trackbacks
OSWorkflow的第一支程式
(史帝芬, 2005/6/4, hi.steven@gmail.com)
自從C的聖經版以Hello World程式開始引導初學者學習程式語言以來,多數的程式語言入門書也都如法刨製。 OSWorkflow沒辦法寫出那種一目瞭然的程式,但是我仍盡量將程式簡化,希望初學者能在看完這第一支程式後, 就能有所體會。接下來將講解如何寫出第一支OSWorkflow程式!
  • jar file

  • 將以下jar file放置到WEB-INF/lib底下…
    activation.jar
    bsh-1.2b7.jar
    commons-logging.jar
    designer.jar
    mail.jar
    oscore-2.2.2.jar
    osuser-1.0-dev-3Dec03.jar
    osworkflow-2.7.0.jar
    propertyset-1.3-21Apr04.jar
    quartz.jar
    這些jar file可以在OSWorkflow提供的example裡找到。
  • 建立資料庫

  • 在SQL Server中建立一資料庫,名稱可自取,這裡取名OSWorkflow,然後執行%OSWorkflow%/src/etc/deployment/jdbc/mssql.sql, 建立OSWorkflow所需的Table。
    然後在Tomcat設定DataSource取名jdbc/DefaultDS並指向資料庫OSWorkflow,方法可參考Connection Pool (2)
    接下來將以下內容放入osworkflow.xml,並將osworkflow.xml放置在WEB-INF/classes底下。
    <osworkflow>
    <!--
    <persistence class="com.opensymphony.workflow.spi.memory.MemoryWorkflowStore"/>
    -->
    <persistence class="com.opensymphony.workflow.spi.jdbc.JDBCWorkflowStore">

    <property key="datasource" value="jdbc/DefaultDS"/>
    <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>
  • 建立流程

  • 建立一個命名為leave.xml的檔案,並將它放入WEB-INF/classes底下,檔名可自取,這個檔案就是記載流程的檔案。
    <?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="開始">
    <results>
    <unconditional-result old-status="Finished" status="Queued" step="1" />
    </results>
    </action>
    </initial-actions>

    <steps>
    <step id="1" name="填假單">
    <actions>
    <action id="1" 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="Queued"
    step="2" owner="User100" />
    </results>
    </action>
    </actions>
    </step>

    <step id="2" name="批假單">
    <actions>
    <action id="2" 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="Queued"
    step="3" owner="${caller}"/>
    </results>
    </action>

    <action id="3" 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="Queued"
    step="1" owner="${caller}"/>
    </results>
    </action>
    </actions>
    </step>

    <step id="3" name="停止" />
    </steps>
    </workflow>
    接著在workflows.xml中加入如下的設定,即可讓系統於啟動後載入請假的流程,workflows.xml也放至WEB-INF/classes。
    <workflows>
    <workflow name="leave" type="resource" location="leave.xml"/>
    </workflows>
  • 程式

  • 這裡將建立兩個網頁分別給員工和主管使用,另外有兩個servlet及一個類別作為趨動流程。
        Leave.java
    package tw.idv.idealist;

    import com.opensymphony.workflow.InvalidActionException;
    import com.opensymphony.workflow.InvalidEntryStateException;
    import com.opensymphony.workflow.InvalidInputException;
    import com.opensymphony.workflow.InvalidRoleException;
    import com.opensymphony.workflow.Workflow;
    import com.opensymphony.workflow.WorkflowException;
    import com.opensymphony.workflow.basic.BasicWorkflow;
    import com.opensymphony.workflow.config.DefaultConfiguration;
    import com.opensymphony.workflow.spi.Step;

    /**
    * 請假流程
    * @author steven
    */
    public class Leave {
    /**
    * 送出假單
    */
    public long send(String employee) {
    Workflow workflow = new BasicWorkflow(employee);
    DefaultConfiguration config = new DefaultConfiguration();
    workflow.setConfiguration(config);
    long workflowId = -1;
    try {
    //leave是workflowx.xml中定的名稱
    workflowId = workflow.initialize("leave", 0, null);
    workflow.doAction(workflowId, 1, null);
    } catch (InvalidActionException e) {
    e.printStackTrace();
    } catch (InvalidRoleException e) {
    e.printStackTrace();
    } catch (InvalidInputException e) {
    e.printStackTrace();
    } catch (InvalidEntryStateException e) {
    e.printStackTrace();
    } catch (WorkflowException e) {
    e.printStackTrace();
    }

    return workflowId;
    }

    /**
    * 准假假單
    * @param workflowId 工作流編號
    * @param actionId 動作編號, 2 准許, 3 駁回
    */
    public void allow(long workflowId, int actionId) {
    Workflow workflow = new BasicWorkflow("manager1");
    DefaultConfiguration config = new DefaultConfiguration();
    workflow.setConfiguration(config);
    try {
    workflow.doAction(workflowId, actionId, null);
    } catch (InvalidInputException e) {
    e.printStackTrace();
    } catch (WorkflowException e) {
    e.printStackTrace();
    }
    catch (InvalidActionException e) {
    e.printStackTrace();
    }
    }
    }
        LeaveAction.java (servlet)
    package tw.idv.idealist.actions;

    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import tw.idv.idealist.Leave;

    /**
    * @author Steven
    */
    public class LeaveAction extends HttpServlet {

    public void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
    doPost(req, resp);
    }

    public void doPost(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
    Leave leave = new Leave();
    String employee = req.getParameter("employee");
    long workflowId = leave.send(employee);
    resp.sendRedirect("allow.jsp?workflowId=" + workflowId);
    }

    }
        AllowAction.java (servlet)
    package tw.idv.idealist.actions;

    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import tw.idv.idealist.Leave;

    /**
    * @author Steven
    */
    public class AllowAction extends HttpServlet {

    public void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
    doPost(req, resp);
    }

    public void doPost(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
    String workflowId = req.getParameter("workflowId");
    String actionId = req.getParameter("actionId");
    Leave leave = new Leave();
    leave.allow(Long.parseLong(workflowId), Integer.parseInt(actionId));
    }

    }
        leave.jsp
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

    <HTML>
    <HEAD>
    <%@ page language="java" contentType="text/html; charset=BIG5"
    pageEncoding="BIG5" %>
    <%@ page import="tw.idv.idealist.*" %>

    <META http-equiv="Content-Type" content="text/html; charset=BIG5">
    <META http-equiv="Content-Style-Type" content="text/css">
    <TITLE>填寫假單</TITLE>
    </HEAD>
    <BODY>
    <P>員工請假</P>

    <form action="LeaveAction">
    員工代號: <input type="text" name="employee">&nbsp;
    <input type="submit" value="送出">
    </form>

    </BODY>
    </HTML>
        allow.jsp
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

    <HTML>
    <HEAD>
    <%@ page language="java" contentType="text/html; charset=BIG5"
    pageEncoding="BIG5" %>

    <META http-equiv="Content-Type" content="text/html; charset=BIG5">
    <META http-equiv="Content-Style-Type" content="text/css">
    <TITLE>批准假單</TITLE>
    <Script Language="JavaScript">
    function DoAction(actionId) {
    document.myForm.actionId.value = actionId;
    document.myForm.submit();
    }
    </Script>
    </HEAD>
    <BODY>

    <%
    String workflowId = request.getParameter("workflowId");
    %>

    <P>主管批假單</P>
    <form action="AllowAction" name="myForm">
    <input type="hidden" name="actionId" value="">
    工作流編號: <input type="text" name="workflowId" value=<%= workflowId %>>
    <input type="button" value="准許" OnClick="DoAction(2)">&nbsp;
    <input type="button" value="駁回" OnClick="DoAction(3)">
    </form>

    </BODY>
    </HTML>
  • 測試

  • 啟動Tomcat,並執行leave.jsp,按"送出"後,假單即送出,接著會出現allow.jsp的網頁供主管簽核,主管按"淮許"後, 請假流程即完成,記得在執行過程中多觀察資料庫中資料的變化。
        請假

        准假

OSWorkflow 觀念探討(1) -- 流程設定基礎

( 史帝芬, 2005/6/6, hi.steven@gmail.com)

看完第一支程式,相信讀者會有許多疑問,這些先就流程的設定做初步的介紹。首先,讀者應該注意到了, 第一支程式的用到三個xml設定檔,沒錯,這三個是設定流程的設定檔。

  • osworkflow.xml :這個檔案是設定流程是否存在資料庫或存在記憶體,前面的內容是存在MS SQL Server的寫法。

 

  • leave.xml :請假流程寫在這個檔案,這也是osworkflow的重點之一。

 

  • workflows.xml :指定系統啟動時要載入那些流程。

 


這裡針對leave.xml做些說明

  • initial-actions :每個流程都至少需定義一個initial-actions,這是流程的起點。

 

  • action :導致流程變動的動作,每個action都有個編號,且不能重複。

 

  • step :雖然它的名稱是步驟,似乎它會有動作? 其實將它視為流程位置可能比較恰當,osworkflow真正的動作在action發生。

 

  • result :執行動作後的結果,result有兩種conditional-resultunconditional-result,每個result一定有unconditional-result, 當conditional-result的條件都不滿足時,就執行unconditional-result

 

  • status :流程在某個action時的狀態。

 

  • old-status :流程執行某個action後的狀態。

 

  • caller :這是OSWorkflow的保留字,可取得呼叫此工作流的user,即Workflow workflow = new BasicWorkflow(caller); 在資料庫中會記錄於Table OS_HISTORYSTEP如下:



記得在pre-functions加上如下設定

<pre-functions>

  <function type="class">

    <arg name="class.name">com.opensymphony.workflow.util.Caller</arg>

  </function>

</pre-functions>

  • owner :這是傳工作流到此步驟的user

 

OSWorkflow 的第二支程式

( 史帝芬, 2005/6/11, hi.steven@gmail.com)

這裡將介紹如何在流程裡加上限制,首先修改leave.xml,限制送出假單的人一定要是啟動流程的人。

<?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="Queued"

step="1" owner="${caller}" />

      </results>

    </action>

  </initial-actions>

 

  <steps>

    <step id="1" name=" 填假單">

      <actions>

        <action id="1" name=" 送出">

          <restrict-to>

            <conditions>

              <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" status="Queued"

step="2"  owner="${caller}" />

          </results>

        </action>

      </actions>

    </step>

 

    <step id="2" name=" 批假單">

      <actions>

        <action id="2" 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="Queued"

step="3"  owner="${caller}"/>

          </results>

        </action>

 

        <action id="3" 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="Queued"

step="1"  owner="${caller}"/>

          </results>

        </action>

      </actions>     

    </step>

 

    <step id="3" name=" 停止" />

  </steps>

</workflow>

接著更改Leave.javaLeaveAction.java,讓啟動流程的人和送出假單的人可以不同。
    Leave.java

package tw.idv.idealist;

 

import com.opensymphony.workflow.InvalidActionException;

import com.opensymphony.workflow.InvalidEntryStateException;

import com.opensymphony.workflow.InvalidInputException;

import com.opensymphony.workflow.InvalidRoleException;

import com.opensymphony.workflow.Workflow;

import com.opensymphony.workflow.WorkflowException;

import com.opensymphony.workflow.basic.BasicWorkflow;

import com.opensymphony.workflow.config.DefaultConfiguration;

import com.opensymphony.workflow.spi.Step;

 

/**

 * 請假流程

 * @author steven

 */

public class Leave {

        /**

         * 啟動流程

         * @param employee 員工編號

         * @return 工作流編號

         */

        public long start(String employee) {

               Workflow workflow = new BasicWorkflow(employee);

               DefaultConfiguration config = new DefaultConfiguration();

               workflow.setConfiguration(config);

               long workflowId = -1;

               try {

                       //leave workflowx.xml中定的名稱

                       workflowId = workflow.initialize("leave", 0, null);

               } catch (InvalidActionException e) {

                       e.printStackTrace();

               } catch (InvalidRoleException e) {

                       e.printStackTrace();

               } catch (InvalidInputException e) {

                       e.printStackTrace();

               } catch (InvalidEntryStateException e) {

                       e.printStackTrace();

               } catch (WorkflowException e) {

                       e.printStackTrace();

               }

              

               return workflowId;

        }

       

        /**

         * 送出假單

         * @param workflowId 工作流編號

         * @param employee 員工編號

         */

        public void send(long workflowId, String employee) {

               Workflow workflow = new BasicWorkflow(employee);

               DefaultConfiguration config = new DefaultConfiguration();

               workflow.setConfiguration(config);

               try {

                       workflow.doAction(workflowId, 1, null);

               } catch (InvalidActionException e) {

                       e.printStackTrace();

               } catch (InvalidRoleException e) {

                       e.printStackTrace();

               } catch (InvalidInputException e) {

                       e.printStackTrace();

               } catch (InvalidEntryStateException e) {

                       e.printStackTrace();

               } catch (WorkflowException e) {

                       e.printStackTrace();

               }

        }

       

        /**

         * 准假假單

         * @param workflowId 工作流編號

         * @param actionId 動作編號, 2 准許, 3 駁回

         */

        public void allow(long workflowId, int actionId) {

               Workflow workflow = new BasicWorkflow("manager1");

               DefaultConfiguration config = new DefaultConfiguration();

               workflow.setConfiguration(config);

               try {

                       workflow.doAction(workflowId, actionId, null);

               } catch (InvalidInputException e) {

                       e.printStackTrace();

               } catch (WorkflowException e) {

                       e.printStackTrace();

               }

               catch (InvalidActionException e) {

                       e.printStackTrace();

               }

        }      

}

    LeaveAction.java

package tw.idv.idealist.actions;

 

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import tw.idv.idealist.Leave;

 

/**

 * @author Steven

 */

public class LeaveAction extends HttpServlet {

 

        public void doGet(HttpServletRequest req, HttpServletResponse resp)

               throws ServletException, IOException {

               doPost(req, resp);

        }

 

        public void doPost(HttpServletRequest req, HttpServletResponse resp)

               throws ServletException, IOException {

               Leave leave = new Leave();

               String employee = req.getParameter("employee");

               long workflowId = leave.start(employee);

               leave.send(workflowId, "someone"); // <<== 非啟動流程的人送出假單

               resp.sendRedirect("allow.jsp?workflowId=" + workflowId);

        }

 

}

package tw.idv.idealist.actions;

 

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import tw.idv.idealist.Leave;

 

/**

 * @author Steven

 */

public class LeaveAction extends HttpServlet {

 

        public void doGet(HttpServletRequest req, HttpServletResponse resp)

               throws ServletException, IOException {

               doPost(req, resp);

        }

 

        public void doPost(HttpServletRequest req, HttpServletResponse resp)

               throws ServletException, IOException {

               Leave leave = new Leave();

               String employee = req.getParameter("employee");

               long workflowId = leave.start(employee);

               leave.send(workflowId, "someone"); // <<== 非啟動流程的人送出假單

               resp.sendRedirect("allow.jsp?workflowId=" + workflowId);

        }

 

}

啟動Tomcat,並執行leave.jsp後,會出現如下exception,確實OSWorkflow偵測到啟動流程的人和送出假單的人不同。

com.opensymphony.workflow.InvalidActionException: Action 1 is invalid

        at com.opensymphony.workflow.AbstractWorkflow.doAction(AbstractWorkflow.java:546)

        at tw.idv.idealist.Leave.send(Leave.java:56)

        at tw.idv.idealist.actions.LeaveAction.doPost(LeaveAction.java:25)

        at tw.idv.idealist.actions.LeaveAction.doGet(LeaveAction.java:17)

        at javax.servlet.http.HttpServlet.service(HttpServlet.java:740)

        at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)

        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:247)

        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)

        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)

        at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)

        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)

        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)

        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)

        at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)

        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)

        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)

        at org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2422)

        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:180)

        at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)

        at org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.java:171)

        at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)

        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:163)

        at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)

        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)

        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)

        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:174)

        at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)

        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)

        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)

        at org.apache.coyote.tomcat4.CoyoteAdapter.service(CoyoteAdapter.java:199)

        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:828)

        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:700)

        at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:584)

        at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:683)

        at java.lang.Thread.run(Thread.java:534)

 

OSWorkflow 觀念探討(2) -- 限制條件

( 史帝芬, 2005/6/11, hi.steven@gmail.com)

第二支程式 只舉AllowOwnerOnlyCondition為例,事實上OSWorkflow 2.7版提供了以下 四種限制條件。

  • OSUserGroupCondition :限制由隸屬某指定Group的人執行。

 

  • StatusCondition :限制stepstatus為某個值時才能執行。

 

  • AllowOwnerOnlyCondition :只允許Owner執行。

 

  • DenyOwnerCondition :只有Owner不能執行。

 

當同時要加上兩個限制條件時,可以如下寫。

<restrict-to>

  <conditions type="AND">

    <condition type="class">

      <arg name="class.name">

         com.opensymphony.workflow.util.StatusCondition

      </arg>

      <arg name="status">Queued</arg>

    </condition>

    <condition type="class">

      <arg name="class.name">

        com.opensymphony.workflow.util.OSUserGroupCondition

      </arg>

      <arg name="group">A0001</arg>

    </condition>

  </conditions>

</restrict-to>

 

OSWorkflow 的第三支程式

( 史帝芬, 2005/6/11, hi.steven@gmail.com)

如前面的請假流程,員工只是請假,但是假有分很多種,事假、病假、婚假、年假,在workflow中如何區分呢? 我們可以傳值到流程中,並儲存在資料庫中供以後判斷!

  • 程式


    leave.xml

<?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>

        <function type="beanshell">

           <arg name="script">

             propertySet.setString("type", transientVars.get("type"));

           </arg>

        </function>

      </pre-functions>

      <results>

        <unconditional-result old-status="Finished" status="Queued"

step="1" owner="${caller}" />

      </results>

      <post-functions>

            <function type="class">

              <arg name="class.name">

                 tw.idv.idealist.OutputPropertySet

              </arg>

              <arg name="author">Steven Shi</arg>

            </function>

      </post-functions>

    </action>

  </initial-actions>

 

  <steps>

    <step id="1" name=" 填假單">

      <actions>

        <action id="1" name=" 送出">

          <restrict-to>

            <conditions>

              <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" status="Queued"

step="2"  owner="${caller}" />

          </results>

        </action>

      </actions>

    </step>

 

    <step id="2" name=" 批假單">

      <actions>

        <action id="2" 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="Queued"

step="3"  owner="${caller}"/>

          </results>

        </action>

 

        <action id="3" 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="Queued"

step="1"  owner="${caller}"/>

          </results>

        </action>

      </actions>      

    </step>

 

    <step id="3" name=" 停止" />

  </steps>

</workflow>

    leave.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

 

<HTML>

<HEAD>

<%@ page language="java" contentType="text/html; charset=BIG5" pageEncoding="BIG5" %>

<%@ page import="tw.idv.idealist.*" %>

 

<META http-equiv="Content-Type" content="text/html; charset=BIG5">

<META http-equiv="Content-Style-Type" content="text/css">

<TITLE> 填寫假單</TITLE>

<Script Language="JavaScript">

function DoAction(type) {

  document.myForm.type.value = type;

  document.myForm.submit();

}

</Script>

</HEAD>

<BODY>

<P> 員工請假</P>

 

<form name="myForm" action="LeaveAction">

  <input type="hidden" name="type" value="">

  員工代號: <input type="text" name="employee"><br>

<input type="button" value=" 事假" OnClick="DoAction(1)">

&nbsp;<input type="button" value=" 病假" OnClick="DoAction(2)">

</form>

 

</BODY>

</HTML>

    Leave.java

package tw.idv.idealist;

 

import java.util.HashMap;

import com.opensymphony.workflow.*;

import com.opensymphony.workflow.basic.BasicWorkflow;

import com.opensymphony.workflow.config.DefaultConfiguration;

import com.opensymphony.workflow.spi.Step;

 

/**

 * 請假流程

 * @author steven

 */

public class Leave {

  /**

   * 啟動流程

   * @param employee 員工編號

   * @param type 假別

   * @return 工作流編號

   */

  public long start(String employee, String type) {

         Workflow workflow = new BasicWorkflow(employee);

         DefaultConfiguration config = new DefaultConfiguration();

         workflow.setConfiguration(config);

         long workflowId = -1;

         try {

                 HashMap map = new HashMap();

                 map.put("type", type);

                 //leave workflowx.xml中定的名稱

                 workflowId = workflow.initialize("leave", 0, map);

         } catch (InvalidActionException e) {

                 e.printStackTrace();

         } catch (InvalidRoleException e) {

                 e.printStackTrace();

         } catch (InvalidInputException e) {

                 e.printStackTrace();

         } catch (InvalidEntryStateException e) {

                 e.printStackTrace();

         } catch (WorkflowException e) {

                 e.printStackTrace();

         }

        

         return workflowId;

  }

 

  /**

   * 送出假單

   * @param workflowId 工作流編號

   * @param employee 員工編號

   */

  public void send(long workflowId, String employee) {

         Workflow workflow = new BasicWorkflow(employee);

         DefaultConfiguration config = new DefaultConfiguration();

         workflow.setConfiguration(config);

         try {

                 workflow.doAction(workflowId, 1, null);

         } catch (InvalidActionException e) {

                 e.printStackTrace();

         } catch (InvalidRoleException e) {

                 e.printStackTrace();

         } catch (InvalidInputException e) {

                 e.printStackTrace();

         } catch (InvalidEntryStateException e) {

                 e.printStackTrace();

         } catch (WorkflowException e) {

                 e.printStackTrace();

         }

  }

 

  /**

   * 准假假單

   * @param workflowId 工作流編號

   * @param actionId 動作編號, 2 准許, 3 駁回

   */

  public void allow(long workflowId, int actionId) {

         Workflow workflow = new BasicWorkflow("manager1");

         DefaultConfiguration config = new DefaultConfiguration();

         workflow.setConfiguration(config);

         try {

                 workflow.doAction(workflowId, actionId, null);

         } catch (InvalidInputException e) {

                 e.printStackTrace();

         } catch (WorkflowException e) {

                 e.printStackTrace();

         }

         catch (InvalidActionException e) {

                 e.printStackTrace();

         }

  }      

}

    LeavaAction.java

package tw.idv.idealist.actions;

 

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import tw.idv.idealist.Leave;

 

/**

 * @author Steven

 */

public class LeaveAction extends HttpServlet {

 

  public void doGet(HttpServletRequest req, HttpServletResponse resp)

         throws ServletException, IOException {

         doPost(req, resp);

  }

 

  public void doPost(HttpServletRequest req, HttpServletResponse resp)

         throws ServletException, IOException {

         Leave leave = new Leave();

         String employee = req.getParameter("employee");

         String type = req.getParameter("type");

         long workflowId = leave.start(employee, type);

         leave.send(workflowId, employee);

         resp.sendRedirect("allow.jsp?workflowId=" + workflowId);

  }

 

}

    OutputPropertySet.java

package tw.idv.idealist;

 

import java.util.Map;

 

import com.opensymphony.module.propertyset.PropertySet;

import com.opensymphony.workflow.FunctionProvider;

import com.opensymphony.workflow.WorkflowException;

 

/**

 * @author steven

 */

public class OutputPropertySet implements FunctionProvider {

  public void execute(Map transientVars, Map args, PropertySet ps)

          throws WorkflowException {

         if (ps.getString("type").equals("1")) {

                 System.out.println(" 事假");

         }

         else if (ps.getString("type").equals("2")) {

                 System.out.println(" 病假");

         }

         System.out.println("author => " + args.get("author"));

  }

}

  • 測試


注意觀察console輸出和資料庫,OutputPropertySet會輸出如下值到console

病假

author => Steven Shi

資料庫Table OS_PROPERTYENTRY則出現如下的值,可以看到假別已存入資料庫。

 

OSWorkflow 觀念探討(3) -- 傳值儲值

( 史帝芬, 2005/6/11, hi.steven@gmail.com)

  • Property sets :持續性變數,會將變數值存入Table OS_PROPERTYENTRY,在設定檔中以propertySet來存取。

 

  • Transient Map :臨時性變數,僅在workflow中有效,在設定檔中以transientVars來存取。

 

  • 外部函數 :外部函數要實作FunctionProvider,如第三支程式OutputPropertySet,在設定檔中如下呼叫使用。

 

<function type="class">

  <arg name="class.name">

    tw.idv.idealist.OutputPropertySet

  </arg>

  <arg name="author">Steven Shi</arg>

</function>

 

OSWorkflow 的第四支程式

( 史帝芬, 2005/6/11, hi.steven@gmail.com)

workflow執行的過程中,有時需要做一些條件的檢查,例如,公司員工編號都是四碼,如果有個工作流的 員工編號只有三碼,就要能檢查出來,下面我們就來做這樣的檢查,各位可以試著用四碼的員工編號請假, 確定可以通過檢查後,再用非四碼的員工編號測試,將會出現最後面顯示的exception

  • 程式


leave.xml

<?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>

        <function type="beanshell">

           <arg name="script">

              propertySet.setString("type", transientVars.get("type"));

              propertySet.setString("employee", transientVars.get("caller"));

           </arg>

        </function>

      </pre-functions>

      <results>

        <unconditional-result old-status="Finished" status="Queued"

step="1" owner="${caller}" />

      </results>

      <post-functions>

            <function type="class">

              <arg name="class.name">

                 tw.idv.idealist.OutputPropertySet

              </arg>

              <arg name="author">Steven Shi</arg>

            </function>

      </post-functions>

    </action>

  </initial-actions>

 

  <steps>

    <step id="1" name=" 填假單">

      <actions>

        <action id="1" name=" 送出">

          <restrict-to>

            <conditions>

              <condition type="class">

                <arg name="class.name">

                  com.opensymphony.workflow.util.AllowOwnerOnlyCondition

                </arg>

              </condition>

            </conditions>

          </restrict-to>

         <validators>

           <validator type="class">

             <arg name="class.name">

               tw.idv.idealist.MyValidator

             </arg>

           </validator>

         </validators>

          <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="Queued"

step="2"  owner="${caller}" />

          </results>

        </action>

      </actions>

    </step>

 

    <step id="2" name=" 批假單">

      <actions>

        <action id="2" 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="Queued"

step="3"  owner="${caller}"/>

        </results>

        </action>

 

        <action id="3" 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="Queued"

step="1"  owner="${caller}"/>

          </results>

        </action>

      </actions>     

    </step>

 

    <step id="3" name=" 停止" />

  </steps>

</workflow>

MyValidator.java

package tw.idv.idealist;

 

import java.util.Map;

import com.opensymphony.module.propertyset.PropertySet;

import com.opensymphony.workflow.InvalidInputException;

import com.opensymphony.workflow.Validator;

import com.opensymphony.workflow.WorkflowException;

 

/**

 * @author steven

 */

public class MyValidator implements Validator {

 

  public void validate(Map transientVars, Map args, PropertySet ps)

         throws InvalidInputException, WorkflowException {

         if (ps.getString("employee").length() != 4) {

                 throw new InvalidInputException(" 員工編號錯誤");

         }

  }

 

}

  • Exception


當輸入的員工編號非四碼時,會有如下錯誤

[InvalidInputException: [Error map: [{}]] [Error list: [[ 員工編號錯誤]]]

  at tw.idv.idealist.MyValidator.validate(MyValidator.java:17)

  at com.opensymphony.workflow.AbstractWorkflow.verifyInputs(AbstractWorkflow.java:1025)

  at com.opensymphony.workflow.AbstractWorkflow.transitionWorkflow(AbstractWorkflow.java:1248)

  at com.opensymphony.workflow.AbstractWorkflow.doAction(AbstractWorkflow.java:551)

  at tw.idv.idealist.Leave.send(Leave.java:55)

  at tw.idv.idealist.actions.LeaveAction.doPost(LeaveAction.java:26)

  at tw.idv.idealist.actions.LeaveAction.doGet(LeaveAction.java:17)

  at javax.servlet.http.HttpServlet.service(HttpServlet.java:740)

  at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)

  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:247)

  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)

  at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)

  at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)

  at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)

  at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)

  at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)

  at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)

  at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)

  at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)

  at org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2422)

  at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:180)

  at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)

  at org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.java:171)

  at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)

  at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:163)

  at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)

  at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)

  at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)

  at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:174)

  at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)

  at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)

  at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)

  at org.apache.coyote.tomcat4.CoyoteAdapter.service(CoyoteAdapter.java:199)

  at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:828)

  at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:700)

  at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:584)

  at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:683)

  at java.lang.Thread.run(Thread.java:534)

 

OSWorkflow 觀念探討(4) -- 校驗器

( 史帝芬, 2005/6/11, hi.steven@gmail.com)

看完第四支程式應該多半的人已經知道怎麼做校驗器了! 這裡再大略講解一下。 首先寫一個類別,這個類別要實作Validator,如前面程式的MyValidator,如有錯誤拋出exception。接下來在流 程設定檔中加入

<validators>

  <validator type="class">

    <arg name="class.name">

      tw.idv.idealist.MyValidator

    </arg>

  </validator>

</validators>

上面這一段設定應放在那裡? 這可以查一下DTD,就知道應放於那個位置了。
<!ELEMENT action (meta*, restrict-to? , validators?, pre-functions?, results, post-functions?)>

 

OSWorkflow 的第五支程式

( 史帝芬, 2005/6/11, hi.steven@gmail.com)

第五支程式要用OSWorkflow提供的API做查詢,查詢出已完成工作流的工作流編號。

  • 程式


Query.java

package tw.idv.idealist;

 

import java.util.*;

import com.opensymphony.workflow.*;

import com.opensymphony.workflow.query.*;

import com.opensymphony.workflow.spi.WorkflowEntry;

 

/**

 * @author steven

 */

public class Query extends AbstractWorkflow {

  public List getCompleted() {

         FieldExpression fe = new FieldExpression(FieldExpression.STATE,

                 FieldExpression.ENTRY,

                 FieldExpression.EQUALS,

                 new Integer(WorkflowEntry.COMPLETED));

         List list = null;

         try {

                 list = getPersistence().query(new WorkflowExpressionQuery(fe));

         } catch (StoreException e) {

                 e.printStackTrace();

         }

 

         return list;

  }

}

completed.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

 

<HTML>

<HEAD>

<%@ page language="java" contentType="text/html; charset=BIG5" pageEncoding="BIG5" %>

<%@ page import="tw.idv.idealist.*" %>

<%@ page import="java.util.*" %>

 

<META http-equiv="Content-Type" content="text/html; charset=BIG5">

<META http-equiv="Content-Style-Type" content="text/css">

<LINK href="theme/Master.css" rel="stylesheet"type="text/css">

<TITLE>completed.jsp</TITLE>

</HEAD>

<BODY>

 

已完成的工作流編號:<br>

<%

Query query = new Query();

List completed = query.getCompleted();

for(int i=0; i<completed.size(); i++) {

  Long wfId = (Long) completed.get(i);

  out.println("workflow id => " + wfId + "<br>");

}

%>

 

</BODY>

</HTML>

  • 測試


 

OSWorkflow 觀念探討(5) -- 查詢API

( 史帝芬, 2005/6/11, hi.steven@gmail.com)

要使用OSWorkflow提供的Query API,先繼承AbstractWorkflow,透過AbstractWorkflow裡的getPersistence method 可連接資料做查詢,其餘的語法參考程式即可知道,這裡針對幾點做說明:

  • WorkflowExpressionQuery 參數


這個類別建立時所需的四個參數,第二個對應到資料庫的Table如下所示,第一個則對應到Table中的欄位, 欄位如何對應由名稱應可明瞭,不另作說明。

常數

Table

FieldExpression.ENTRY

OS_WFENTRY

FieldExpression.CURRENT_STEPS

OS_CURRENTSTEP

FieldExpression.HISTORY_STEPS

OS_HISTORYSTEP


第三個參數為運算元,只有四種如下:

運算元

說明

FieldExpression.EQUALS

等於

FieldExpression.NOT_EQUALS

不等於

FieldExpression.GT

大於

FieldExpression.LT

小於

 

  • 巢狀運算


如果要比較的不只一個欄位,就需要用到巢狀運算,使用class NestedExpression,底下是一個官方文件的範例:

// Get all finished workflow entries

//where the current owner is 'testuser'

Expression queryLeft = new FieldExpression(

  FieldExpression.OWNER,

  FieldExpression.CURRENT_STEPS,

  FieldExpression.EQUALS, 'testuser');

Expression queryRight = new FieldExpression(

  FieldExpression.STATUS,

  FieldExpression.CURRENT_STEPS,

  FieldExpression.EQUALS,

  "Finished",

  true);

WorkflowExpressionQuery query =

  new WorkflowExpressionQuery(

    new NestedExpression(

      new Expression[] {queryLeft, queryRight},

        NestedExpression.AND));

 

posted on 2006-08-15 16:25 Alex 阅读(4326) 评论(0)  编辑  收藏 所属分类: workflow

只有注册用户登录后才能发表评论。


网站导航: