﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-寻找我的礼物-文章分类-他山之石</title><link>http://www.blogjava.net/hrcdg/category/23957.html</link><description>人生就是在不断洗牌，几年河东，几年河西</description><language>zh-cn</language><lastBuildDate>Sat, 04 Aug 2007 22:34:31 GMT</lastBuildDate><pubDate>Sat, 04 Aug 2007 22:34:31 GMT</pubDate><ttl>60</ttl><item><title>[转]osworkflow代码分析1：主要接口</title><link>http://www.blogjava.net/hrcdg/articles/134163.html</link><dc:creator>Eric huang</dc:creator><author>Eric huang</author><pubDate>Fri, 03 Aug 2007 01:52:00 GMT</pubDate><guid>http://www.blogjava.net/hrcdg/articles/134163.html</guid><wfw:comment>http://www.blogjava.net/hrcdg/comments/134163.html</wfw:comment><comments>http://www.blogjava.net/hrcdg/articles/134163.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hrcdg/comments/commentRss/134163.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hrcdg/services/trackbacks/134163.html</trackback:ping><description><![CDATA[<h4 class=TextColor1 id=subjcns!41C07CC57E95C410!124 style="MARGIN-BOTTOM: 0px">osworkflow代码分析1：主要接口</h4>
<div class=bvMsg id=msgcns!41C07CC57E95C410!124>
<p>这几天抽了点时间看了一下osworkflow 的源代码，简单对几个主要的接口作用进行分析。
<p>1.com.opensymphony.workflow.Workflow 工作流的用户接口。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 主要定义了用户对工作流的操作方法和用户获得工作流信息的方法。如doAction(long id, int actionId, Map inputs)方法可以执行工作流的Action并产生transaction；用户调用getAvailableActions(long id, Map inputs)可以获得知道工作流实例中符合条件的可以执行的Action。
<p>2.com.opensymphony.workflow.WorkflowContext 工作流的Context接口。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 只有两个方法，其中getCaller()获得调用者，setRollbackOnly()可以回滚Action造成的transaction。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setRollbackOnly()方法非常重要，可以在此方法中实现工作流数据与业务数据的事务处理。由于工作流引擎将流程数据与业务数据分离开管理，所以工作流数据与业务数据之间的事务处理往往比较困难，甚至有很多商业的工作流引擎都没有解决这个问题，造成软件上的漏洞。可惜在BasicWorkflowContext中并没有实现回滚时的事务处理，但实现起来应该不会很困难，在以后会单独考虑。
<p>3.com.opensymphony.workflow.spi.WorkflowEntry 工作流实例的接口。
<p>&nbsp;&nbsp;&nbsp;&nbsp; 定义了获得工作流实例信息的方法。
<p>4.com.opensymphony.workflow.config.Configuration 工作流配置接口。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 获得osworkflw的配置信息和流程的定义信息， osworkflow中的例子就是使用此接口的默认实现。如果想让osworkflw与自己的系统更好的整合，这个接口需要自己实现。
<p>5.com.opensymphony.workflow.loader.AbstractWorkflowFactory 流程定义的解析器。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; osworkflow中提供了此抽象类的3种实现，最常用的是XMLWorkflowFactory，可以对编写的工作流定义xml文件进行解析。
<p>6.com.opensymphony.workflow.spi.WorkflowStore 工作流存储接口。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;实现此接口可以实现用多种途径保存工作流信息，jdbc,hibernate,ejb,memory.........</p>
</div>
<img src ="http://www.blogjava.net/hrcdg/aggbug/134163.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hrcdg/" target="_blank">Eric huang</a> 2007-08-03 09:52 <a href="http://www.blogjava.net/hrcdg/articles/134163.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]osworkflow代码分析2:AbstractWorkflow类</title><link>http://www.blogjava.net/hrcdg/articles/134162.html</link><dc:creator>Eric huang</dc:creator><author>Eric huang</author><pubDate>Fri, 03 Aug 2007 01:50:00 GMT</pubDate><guid>http://www.blogjava.net/hrcdg/articles/134162.html</guid><wfw:comment>http://www.blogjava.net/hrcdg/comments/134162.html</wfw:comment><comments>http://www.blogjava.net/hrcdg/articles/134162.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hrcdg/comments/commentRss/134162.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hrcdg/services/trackbacks/134162.html</trackback:ping><description><![CDATA[<h4 class=TextColor1 id=subjcns!41C07CC57E95C410!131 style="MARGIN-BOTTOM: 0px">osworkflow代码分析2:AbstractWorkflow类</h4>
<div class=bvMsg id=msgcns!41C07CC57E95C410!131>
<p>AbstractWorkflow类是workflow接口的最基本的实现。
<p>1.public int[] getAvailableActions(long id, Map inputs)方法：
<p>返回当前可以执行的Ation。
<ul>
    <li>得到工作流流程实例。
    <li>得到工作流实例的定义。
    <li>得到工作流实例的PropertySet。
    <li>得到工作流的当前Step。
    <li>产生TransientVars。
    <li>得到Global Actions。
    <li>判断可以执行的Global Action增加到可执行Action列表中。
    <li>获得当前Steps中的可执行Action并添加到可执行Action列表中。
    <li>返回可执行Actions。 </li>
</ul>
<p>2. public void setConfiguration(Configuration configuration)方法：
<p>设置工作流配置方法。
<p>3.public Configuration getConfiguration()方法：
<p>返回工作流配置方法，如果没有获得配置信息，初始化配置信息。
<p>4.public List getCurrentSteps(long id)：
<p>获得工作流当前所在步骤。
<p>5.public int getEntryState(long id):
<p>获得工作流的状态。
<p>6.public List getHistorySteps(long id)
<p>获得工作流的历史步骤。
<p>7. public Properties getPersistenceProperties()
<p>获得设置的持久化参数。
<p>8.public PropertySet getPropertySet(long id)
<p>得到工作流的PropertySet，调用store中的方法。
<p>9.public List getSecurityPermissions(long id)
<p>得到工作流当前Step的permissions。
<p>10.public WorkflowDescriptor getWorkflowDescriptor(String workflowName)
<p>得到工作流的定义。
<p>11.public String getWorkflowName(long id)
<p>根据工作流实例返回工作流定义名。
<p>12. public String[] getWorkflowNames()
<p>返回系统中配置的所有工作流的名字。
<p>13.public boolean canInitialize(String workflowName, int initialAction)，public boolean canInitialize(String workflowName, int initialAction, Map inputs)，private boolean canInitialize(String workflowName, int initialAction, Map transientVars, PropertySet ps) throws WorkflowException
<p>判断指定的工作流初始化Action是不是可以执行。
<p>14.public boolean canModifyEntryState(long id, int newState)
<p>判断工作流是不是可以转换到指定状态。
<ul>
    <li>不可以转换到CREATED状态。
    <li>CREATED，SUSPENDED可以转换到ACTIVATED状态。
    <li>ACTIVATED可以转换到SUSPENDED状态。
    <li>CREATED，ACTIVATED，SUSPENDED&nbsp;可以转换到KILLED状态。 </li>
</ul>
<p>15.public void changeEntryState(long id, int newState) throws WorkflowException
<p>转换工作流状态。
<p>16.public void doAction(long id, int actionId, Map inputs) throws WorkflowException
<p>执行Action。
<ul>
    <li>获得工作流store，和流程实例entry。
    <li>判断是不是活动的工作流，不是就返回。
    <li>获得工作流的定义。
    <li>获得工作流当前所再Steps。
    <li>获得工作流PropertySet。
    <li>生成transientVars。
    <li>从GlobalActions中和当前Steps的普通Actions中判断执行的Action是否试可执行的。
    <li>完成Action的Transition。 </li>
</ul>
<p>17.public void executeTriggerFunction(long id, int triggerId) throws WorkflowException
<p>调用工作流的Trigger Function
<p>18.public long initialize(String workflowName, int initialAction, Map inputs) throws InvalidRoleException, InvalidInputException, WorkflowException
<p>初始化一个新的流程实例。返回流程实例id。
<p>19.public List query(WorkflowQuery query)，public List query(WorkflowExpressionQuery query)
<p>查询流程实例。
<p>20.public boolean removeWorkflowDescriptor(String workflowName) throws FactoryException
<p>删除已经配置的工作流定义。
<p>21.public boolean saveWorkflowDescriptor(String workflowName, WorkflowDescriptor descriptor, boolean replace) throws FactoryException
<p>保存工作流定义。
<p>22.protected List getAvailableActionsForStep(WorkflowDescriptor wf, Step step, Map transientVars, PropertySet ps) throws WorkflowException
<p>获得指定步骤的可用Actions。
<p>23.protected int[] getAvailableAutoActions(long id, Map inputs)
<p>返回可执行的AutoActions。
<p>24.protected List getAvailableAutoActionsForStep(WorkflowDescriptor wf, Step step, Map transientVars, PropertySet ps) throws WorkflowException
<p>返回指定Step中可执行的AutoActions。
<p>25.protected WorkflowStore getPersistence() throws StoreException
<p>返回配置的store。
<p>26.protected void checkImplicitFinish(long id) throws WorkflowException
<p>判断工作流是不是还有可执行的Action，如果没有，完成此工作流实例。
<p>27.protected void completeEntry(long id, Collection currentSteps) throws StoreException
<p>结束工作流实例，就是把改变流程实例的状态并把当前的Steps都放入到历史表中。&nbsp;
<p>28.protected boolean passesCondition(ConditionDescriptor conditionDesc, Map transientVars, PropertySet ps, int currentStepId) throws WorkflowException
<p>29.protected boolean passesCondition(ConditionDescriptor conditionDesc, Map transientVars, PropertySet ps, int currentStepId) throws WorkflowException，protected boolean passesConditions(String conditionType, List conditions, Map transientVars, PropertySet ps, int currentStepId) throws WorkflowException
<p>判断条件是不是符合。
<p>30.protected void populateTransientMap(WorkflowEntry entry, Map transientVars, List registers, Integer actionId, Collection currentSteps) throws WorkflowException
<p>产生临时变量transientVars，包含context，entry，store，descriptor，actionId，currentSteps，以及定义的register和用户的输入变量。
<p>31.protected void verifyInputs(WorkflowEntry entry, List validators, Map transientVars, PropertySet ps) throws WorkflowException
<p>验证用户的输入。
<p>32.private boolean isActionAvailable(ActionDescriptor action, Map transientVars, PropertySet ps, int stepId) throws WorkflowException
<p>判断Action是否可用。
<p>33.private Step getCurrentStep(WorkflowDescriptor wfDesc, int actionId, List currentSteps, Map transientVars, PropertySet ps) throws WorkflowException
<p>获得Action所在Step。
<p>34.private boolean canInitialize(String workflowName, int initialAction, Map transientVars, PropertySet ps) throws WorkflowException
<p>判断工作流是不是可以实例化。
<p>35.private Step createNewCurrentStep(ResultDescriptor theResult, WorkflowEntry entry, WorkflowStore store, int actionId, Step currentStep, long[] previousIds, Map transientVars, PropertySet ps) throws WorkflowException
<p>产生新的当前Step。
<ul>
    <li>从resulte中获得nextStep，如果为-1，nextStep为当前Step。
    <li>获得定义中的owner，oldStatus，status。
    <li>完成当前Step，并且将当前Step保存到历史库中。
    <li>生成新的Step。 </li>
</ul>
<p>36.private void executeFunction(FunctionDescriptor function, Map transientVars, PropertySet ps) throws WorkflowException
<p>执行Function。
<p>37.private boolean transitionWorkflow(WorkflowEntry entry, List currentSteps, WorkflowStore store, WorkflowDescriptor wf, ActionDescriptor action, Map transientVars, Map inputs, PropertySet ps) throws WorkflowException
<p>完成工作流的transation。</p>
</div>
<img src ="http://www.blogjava.net/hrcdg/aggbug/134162.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hrcdg/" target="_blank">Eric huang</a> 2007-08-03 09:50 <a href="http://www.blogjava.net/hrcdg/articles/134162.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]解读osworkflow1~9 </title><link>http://www.blogjava.net/hrcdg/articles/133760.html</link><dc:creator>Eric huang</dc:creator><author>Eric huang</author><pubDate>Wed, 01 Aug 2007 03:19:00 GMT</pubDate><guid>http://www.blogjava.net/hrcdg/articles/133760.html</guid><wfw:comment>http://www.blogjava.net/hrcdg/comments/133760.html</wfw:comment><comments>http://www.blogjava.net/hrcdg/articles/133760.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hrcdg/comments/commentRss/133760.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hrcdg/services/trackbacks/133760.html</trackback:ping><description><![CDATA[<div class=post>
<h3 class=xstorytitle id=post-79><a title="Permanent Link: OSWorkflow解读之九" href="http://morningspace.51.net/weblog/index.php?p=79" rel=bookmark><u><font color=#0000ff>OSWorkflow解读之九</font></u></a></h3>
<div class=meta>Filed under:
<ul class=post-categories>
    <li><a title="View all posts in Workflow" href="http://morningspace.51.net/weblog/index.php?cat=6"><font color=#0000ff size=2><u>Workflow</u></font></a> </li>
</ul>
— site admin @ 9:08 am </div>
<div class=storycontent>
<p>UserManager&gt;&gt;</p>
<p>OSWorkflow在用户管理方面所提供的功能，主要包括用户的创建、群组的定义、用户验证、以及对step执行人的跟踪记录和执行权限的判断等等。</p>
<p>用户/群组的管理是由UserManager来完成的，它包含于一个单独的jar包内。我们可以这样使用UserManager：</p>
<p class=prettycode>UserManager um = UserManager.getInstance();<br>User test = um.createUser("test");<br>test.setPassword("test");<br>Group foos = um.createGroup("foos");<br>test.addToGroup(foos); </p>
<p>利用UserManager也可以实现用户验证功能：</p>
<p class=prettycode>UserManager um = UserManager.getInstance();<br>boolean authenticated = false;<br>authenticated = um.getUser(username).authenticate(password);<br>if (authenticated) {<br>　session.setAttribute("username");<br>　&#8230;&#8230;<br>} else {<br>　&#8230;&#8230;<br>} </p>
<p>关于step执行人的跟踪，首先我们可以在创建流程的时候传入调用者（caller）名称，比如：</p>
<p class=prettycode>Workflow wf = new BasicWorkflow((String) session.getAttribute("username")); </p>
<p>BasicWorkflow会负责创建一个实现了WorkflowContext接口的实例，其中记录了caller的信息。利用com.opensymphony.workflow.util.Caller，可以将WorkflowContext中的caller随时植入transientVars中，以供后续的条件判断。为此，我们需要在流程定义文件中的适当位置加入如下定义（比如initial-actions中的pre-functions节点处）：</p>
<p class=prettycode>&lt;pre-functions&gt;<br>　&lt;function type="class"&gt;<br>　　&lt;arg name="class.name"&gt;com.opensymphony.workflow.util.Caller&lt;/arg&gt;<br>　&lt;/function&gt;<br>&lt;/pre-functions&gt; </p>
<p>Caller是一个FunctionProvider，其excute方法中包含了如下代码：</p>
<p class=prettycode>WorkflowContext context = (WorkflowContext) transientVars.get("context");<br>transientVars.put("caller", context.getCaller()); </p>
<p>同时，我们还可以指定流程中某个step的执行人（owner），只需要在action的results节点处为其指定owner属性：</p>
<p class=prettycode>&lt;step id="2&#8243; name="Edit Doc"&gt;<br>　&lt;actions&gt;<br>　　&lt;action id="2&#8243; name="Sign Up For Editing"&gt;<br>　　　&#8230;&#8230;<br>　　　&lt;results&gt;<br>　　　　&lt;unconditional-result old-status="Finished&#8221; status="Underway&#8221; step="2&#8243; owner="${caller}"/&gt;<br>　　　&lt;/results&gt; </p>
<p>利用caller和owner信息，我们可以在流程定义文件的condition节点中以多种形式指定限定条件，比如，利用脚本限定只允许caller为test的用户触发某结果：</p>
<p class=prettycode>&lt;result old-status="Finished"&gt;<br>　&lt;condition type="beanshell"&gt;<br>　　&lt;arg name="script"&gt;<br>　　　propertySet.getString("caller").equals("test")<br>　　&lt;/arg&gt;<br>　&lt;/condition&gt;<br>　&#8230;&#8230;<br>&lt;/result&gt; </p>
<p>又比如，利用util包中的OSUserGroupCondition限定仅当caller为foos群组中的用户时，才触发action：</p>
<p class=prettycode>&lt;action id="1&#8243; name="Start Workflow"&gt;<br>　&#8230;&#8230;<br>　&lt;condition type="class"&gt;<br>　　&lt;arg name="class.name"&gt;com.opensymphony.workflow.util.OSUserGroupCondition&lt;/arg&gt;<br>　　&lt;arg name="group"&gt;foos&lt;/arg&gt;<br>　&lt;/condition&gt; </p>
<p>再比如：利用util包中的AllowOwnerOnlyCondition限定仅当caller等于owner时，才触发action：</p>
<p class=prettycode>&lt;action id="1&#8243; name="Start Workflow"&gt;<br>　&#8230;&#8230;<br>　&lt;condition type="class"&gt;<br>　　&lt;arg name="class.name"&gt;com.opensymphony.workflow.util.AllowOwnerOnlyCondition&lt;/arg&gt;<br>　&lt;/condition&gt; </p>
</div>
<div class=feedback><a href="http://morningspace.51.net/weblog/index.php?p=79#comments"><font color=#667755><u>Comments (0)</u></font></a> </div>
<!--	<rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 	    xmlns:dc="http://purl.org/dc/elements/1.1/"	    xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">		<rdf:description rdf:about="http://morningspace.51.net/weblog/index.php?p=79"    dc:identifier="http://morningspace.51.net/weblog/index.php?p=79"    dc:title="OSWorkflow解读之九"    trackback:ping="http://morningspace.51.net/weblog/wp-trackback.php/79" /></rdf:rdf>	--></div>
<div class=date>11/29/2004</div>
<div class=post>
<h3 class=xstorytitle id=post-78><a title="Permanent Link: OSWorkflow解读之八" href="http://morningspace.51.net/weblog/index.php?p=78" rel=bookmark><u><font color=#0000ff>OSWorkflow解读之八</font></u></a></h3>
<div class=meta>Filed under:
<ul class=post-categories>
    <li><a title="View all posts in Workflow" href="http://morningspace.51.net/weblog/index.php?cat=6"><font color=#0000ff size=2><u>Workflow</u></font></a> </li>
</ul>
— site admin @ 10:21 am </div>
<div class=storycontent>
<p>WorkflowQuery及其有关查询类&gt;&gt;</p>
<p>我们知道，通常人们总是希望了解流程当前的运行状况，因此就需要工作流引擎在提供流程流转的基本功能的同时，还需要提供查询功能。在osworkflow中，查询功能是由WorkflowQuery及其相关类提供的。</p>
<p>WorkflowQuery提供了两种类型的构造函数：</p>
<p class=prettycode>public WorkflowQuery(int field, int type, int operator, Object value)<br>public WorkflowQuery(WorkflowQuery left, int operator, WorkflowQuery right) </p>
<p>我们可以利用第一个构造函数创建基本的WorkflowQuery实例，然后利用第二个构造函数组织装配。以查询执行者是&#8220;test&#8221;且状态是&#8220;Underway&#8221;的step实例为例：</p>
<p class=prettycode>WorkflowQuery queryLeft = new WorkflowQuery(<br>　　WorkflowQuery.OWNER, WorkflowQuery.CURRENT, WorkflowQuery.EQUALS, &#8220;test");<br>WorkflowQuery queryRight = new WorkflowQuery(<br>　　WorkflowQuery.STATUS, WorkflowQuery.CURRENT, WorkflowQuery.EQUALS, &#8220;Underway");<br>WorkflowQuery query = new WorkflowQuery(<br>　　queryLeft, WorkflowQuery.AND, queryRight);<br>List workflows = wf.query(query);<br>for (Iterator iterator = workflows.iterator(); iterator.hasNext();)<br>　　Long wfId = (Long) iterator.next();<br>} </p>
<p>装配好的查询条件，将会传入AbstractoWorkflow的query方法中，然后再由WorkflowStore的query方法执行具体查询操作。不同的WorkflowStore实例其查询方式不尽相同，以MemoryWorkflowStore为例，它将遍历所有位于cache中的流程，然后将满足条件的流程ID放入一个ArrayList中返回，查询的核心代码采用了递归调用的形式：</p>
<p class=prettycode>if (query.getLeft() == null) {<br>　return queryBasic(entryId, query);<br>} else {<br>　int operator = query.getOperator();<br>　WorkflowQuery left = query.getLeft();<br>　WorkflowQuery right = query.getRight();<br>　switch (operator) {<br>　　case WorkflowQuery.AND:<br>　　　return query(entryId, left) &amp;&amp; query(entryId, right);<br>　　case WorkflowQuery.OR:<br>　　　return query(entryId, left) || query(entryId, right);<br>　　case WorkflowQuery.XOR:<br>　　　return query(entryId, left) ^ query(entryId, right);<br>　}<br>} </p>
<p>这里的queryBasic再次使用了递归调用的方式，详细情况可以查看osworkflow的源代码。通过这样的方式，可以满足任意复杂的流程查询条件指定。 </p>
</div>
<div class=feedback><a href="http://morningspace.51.net/weblog/index.php?p=78#comments"><font color=#667755><u>Comments (0)</u></font></a> </div>
<!--	<rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 	    xmlns:dc="http://purl.org/dc/elements/1.1/"	    xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">		<rdf:description rdf:about="http://morningspace.51.net/weblog/index.php?p=78"    dc:identifier="http://morningspace.51.net/weblog/index.php?p=78"    dc:title="OSWorkflow解读之八"    trackback:ping="http://morningspace.51.net/weblog/wp-trackback.php/78" /></rdf:rdf>	--></div>
<div class=date>11/28/2004</div>
<div class=post>
<h3 class=xstorytitle id=post-74><a title="Permanent Link: OSWorkflow解读之七" href="http://morningspace.51.net/weblog/index.php?p=74" rel=bookmark><u><font color=#0000ff>OSWorkflow解读之七</font></u></a></h3>
<div class=meta>Filed under:
<ul class=post-categories>
    <li><a title="View all posts in Workflow" href="http://morningspace.51.net/weblog/index.php?cat=6"><font color=#0000ff size=2><u>Workflow</u></font></a> </li>
</ul>
— site admin @ 4:32 pm </div>
<div class=storycontent>
<p>Descriptors&gt;&gt;</p>
<p>在osworkflow的很多地方都会看到Descriptor的使用，最重要的一个是前面提到的WorkflowDescriptor。除此以外还有，ActionDescriptor、ConditionsDescriptor、ConditionDescriptor、FunctionDescriptor、PermissionDescriptor等等。这些类均位于com.opensymphony.workflow.loader包中。它们的作用，除了提供getter方法外（类似model的角色），还负责xml文件的读取与写入。WorkflowDescriptor在执行xml文件的读写时，如果涉及具体的流程定义元素，将会交由对应的Descriptor类来完成。比如，在WorkflowDescriptor的writeXML方法中，对initial action的序列化是这么实现的：</p>
<p class=prettycode>XMLUtil.printIndent(out, indent++);<br>out.println("&lt;initial-actions&gt;");<br>for (int i = 0; i &lt; initialActions.size(); i++) {<br>　ActionDescriptor action = (ActionDescriptor) initialActions.get(i);<br>　action.writeXML(out, indent);<br>}<br>XMLUtil.printIndent(out, &#8211;indent);<br>out.println("&lt;/initial-actions&gt;"); </p>
<p>实际上，这是典型的Interpreter Pattern的运用，下面是类图总结。<br><a title=点击查看大图 href="http://morningspace.51.net/weblog/wp-content/osworkflow-desc.jpg"></a></p>
</div>
<div class=feedback><a href="http://morningspace.51.net/weblog/index.php?p=74#comments"><font color=#667755><u>Comments (0)</u></font></a> </div>
<!--	<rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 	    xmlns:dc="http://purl.org/dc/elements/1.1/"	    xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">		<rdf:description rdf:about="http://morningspace.51.net/weblog/index.php?p=74"    dc:identifier="http://morningspace.51.net/weblog/index.php?p=74"    dc:title="OSWorkflow解读之七"    trackback:ping="http://morningspace.51.net/weblog/wp-trackback.php/74" /></rdf:rdf>	--></div>
<div class=date>11/27/2004</div>
<div class=post>
<h3 class=xstorytitle id=post-71><a title="Permanent Link: OSWorkflow解读之六" href="http://morningspace.51.net/weblog/index.php?p=71" rel=bookmark><u><font color=#0000ff>OSWorkflow解读之六</font></u></a></h3>
<div class=meta>Filed under:
<ul class=post-categories>
    <li><a title="View all posts in Workflow" href="http://morningspace.51.net/weblog/index.php?cat=6"><font color=#0000ff size=2><u>Workflow</u></font></a> </li>
</ul>
— site admin @ 10:53 am </div>
<div class=storycontent>
<p>pre function和post function&gt;&gt;</p>
<p>pre function和post function是osworkflow提供的又一特色，它为某项执行逻辑提供了前驱和后继处理，运用十分灵活。并且，osworkflow为许多元件的执行逻辑都配备了pre function和post function的调用时机。这一点也可以从AbstractWorkflow.doAction的执行逻辑中看到。可以使用和pre function和post function的元件包括：action，result/unconditional result，step，split，join。</p>
<p>ScriptVariableParser&gt;&gt;</p>
<p>作为osworkflow的一个util class，ScriptVariableParser的主要功能是将给定字串中的${var}替换成相应的value。这意味着我们可以在许多地方使用类似于Ant中引用property的语法，来进一步提高灵活性。比如：<br>&lt;results&gt;<br>　&lt;unconditional -result old-status="Finished&#8221; status="Underway&#8221; step="1&#8243; owner="${caller}"/&gt;<br>&lt;/results&gt;<br>在这里，unconditional result的owner属性将被caller的实际值所替代。</p>
<p>TransientVars和PropertySet&gt;&gt;</p>
<p>在osworkflow的流程流转过程中，时常会用到Transient Vars和Property Set。这两个工具用来暂存一些临时信息或者在step间传递一些共享信息，比如：context信息，workflow entry信息，以及上面提到的${var}的value，等等。</p>
<p>Transient Vars实际上就是一个普通的Map，至于Property Set，则是opensymphony的另一个独立模块，需要单独下载jar包。与Transient Vars将信息暂存与内存不同，Property Set还支持数据库存储（JDBCPropertySet）</p>
<p>Register&gt;&gt;</p>
<p>为了更进一步提高灵活性，osworkflow还提供了Register功能。我们可以定义自己的Register，以执行特殊任务，并在流程定义文件中注明，该Register便会被动态注册到Transient Vars中，以备随时取用。以下便是一个典型的使用场景：</p>
<div class="prettycode ">&lt;registers&gt;<br>　&lt;register type="class&#8221; variable-name="log"&gt;<br>　　&lt;arg name="class.name"&gt;com.opensymphony.workflow.util.LogRegister&lt;/arg&gt;<br>　　&lt;arg name="addInstanceId"&gt;true&lt;/arg&gt;<br>　&lt;/register&gt;<br>&lt;/registers&gt; </div>
<div class="prettycode ">&lt;function type="beanshell&#8221; name="bsh.function"&gt;<br>　&lt;arg name="script"&gt;transientVars.get("log").info("function called");&lt;/arg&gt;<br>&lt;/function&gt; </div>
<p>此外，为了方便使用，osworkflow的util包中还预定义了大量的Condition和FunctionProvider，以及其他的一些辅助类，比如：StatusCondition、AllowOwnerOnlyCondition、BeanShellCondition、Caller、EJBInvoker、ScheduleJob。</p>
<p>借助这些设施，osworkflow的扩展性、灵活性、易用性，得到了极大的体现。 </p>
</div>
<div class=feedback><a href="http://morningspace.51.net/weblog/index.php?p=71#comments"><font color=#667755><u>Comments (0)</u></font></a> </div>
<!--	<rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 	    xmlns:dc="http://purl.org/dc/elements/1.1/"	    xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">		<rdf:description rdf:about="http://morningspace.51.net/weblog/index.php?p=71"    dc:identifier="http://morningspace.51.net/weblog/index.php?p=71"    dc:title="OSWorkflow解读之六"    trackback:ping="http://morningspace.51.net/weblog/wp-trackback.php/71" /></rdf:rdf>	--></div>
<div class=date>11/26/2004</div>
<div class=post>
<h3 class=xstorytitle id=post-75><a title="Permanent Link: OSWorkflow解读之五" href="http://morningspace.51.net/weblog/index.php?p=75" rel=bookmark><u><font color=#0000ff>OSWorkflow解读之五</font></u></a></h3>
<div class=meta>Filed under:
<ul class=post-categories>
    <li><a title="View all posts in Workflow" href="http://morningspace.51.net/weblog/index.php?cat=6"><font color=#0000ff size=2><u>Workflow</u></font></a> </li>
</ul>
— site admin @ 12:00 am </div>
<div class=storycontent>
<p>Schedule&gt;&gt;</p>
<p>在osworkflow中提供了定时执行某项任务的功能，这主要得益于opensymphony的另一个项目——Quatz，该项目为工作的定期执行提供了底层设施支持。为此，我们需要引用quatrz.jar包，好在osworkflow的下载包中已经包含了该文件。</p>
<p>为了实现任务定时，我们需要在流程定义文件中做类似如下的配置：</p>
<div class=prettycode>&lt;function type="class"&gt;<br>　&lt;arg name="class.name"&gt;com.opensymphony.workflow.util.ScheduleJob&lt;/arg&gt;<br>　&lt;arg name="triggerId"&gt;1&lt;/arg&gt;<br>　&lt;arg name="jobName"&gt;testJob&lt;/arg&gt;<br>　&lt;arg name="triggerName"&gt;testTrigger&lt;/arg&gt;<br>　&lt;arg name="groupName"&gt;test&lt;/arg&gt;<br>　&lt;arg name="repeat"&gt;10&lt;/arg&gt;<br>　&lt;arg name="repeatDelay"&gt;2000&lt;/arg&gt;<br>　&lt;arg name="cronExpression"&gt;0,5,10,15,20,25,30,35,40,45,50,55 * * * * ?&lt;/arg&gt;<br>　&lt;arg name="username"&gt;test&lt;/arg&gt;<br>　&lt;arg name="password"&gt;test&lt;/arg&gt;<br>　&lt;arg name="local"&gt;true&lt;/arg&gt;<br>　&lt;arg name="schedulerStart"&gt;true&lt;/arg&gt;<br>&lt;/function&gt; </div>
<p>ScheduleJob是一个FunctionProiver，因此具有execute方法。在该方法执行期间，ScheduleJob将会读取这些配置参数，创建好job实例（实际上是一个JobDetail实例）和trigger实例，然后启动schedule。大致流程如下：<br>- 根据传入的shedulerName参数，利用org.quartz.impl.StdSchedulerFactory的getScheduler方法创建sheduler实例，该实例实现了org.quartz.Scheduler接口；<br>- 根据传入的jobClass参数，决定创建何种Job实例，osworkflow自身提供了两种选择：WorkflowJob和LocalWorkflowJob。前者支持SOAP协议，后者则是本地调用，它们都实现了org.quartz.Job接口。<br>- 创建一个描述Job信息的JobDetail实例，并做好初始设置；<br>- 若传入参数中未指定cronExpression，则创建SimpleTrigger，并设置好startDate、endDate、repeat，否则创建CronTrigger<br>- 在jobDetail和trigger准备完毕后，就可以启动schedule了：</p>
<p class=prettycode>s.addJob(jobDetail, true);<br>s.scheduleJob(trigger);<br>s.start(); </p>
<p>scheduler中应该可以同时维护多个job和trigger，当trigger的触发条件满足后，将会激活真正的job实例。由于，scheduler中只保存了jobDetail的实例，因此我猜想，job实例的真正创建是由jobDetail完成的。job实例（WorkflowJob、LocalWorkflowJob或者是其他自定义扩展类）激活后，其excute方法将会被执行。其内部的执行逻辑大体是获得指定的Workflow实例，然后执行该实例的executeTriggerFunction方法。trigger function的执行与先前在流程定义文件中所出现过的普通function大同小异。当然，我们还需要在流程定义文件中加入对trigger function的描述，大致格式如下：</p>
<p class=prettycode>&lt;trigger-functions&gt;<br>　&lt;trigger-function id="1&#8243; &gt;<br>　&lt;function&gt;<br>　　&#8230;<br>　&lt;/function&gt;<br>&lt;/trigger-functions&gt; </p>
</div>
<div class=feedback><a href="http://morningspace.51.net/weblog/index.php?p=75#comments"><font color=#667755><u>Comments (0)</u></font></a> </div>
<!--	<rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 	    xmlns:dc="http://purl.org/dc/elements/1.1/"	    xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">		<rdf:description rdf:about="http://morningspace.51.net/weblog/index.php?p=75"    dc:identifier="http://morningspace.51.net/weblog/index.php?p=75"    dc:title="OSWorkflow解读之五"    trackback:ping="http://morningspace.51.net/weblog/wp-trackback.php/75" /></rdf:rdf>	--></div>
<div class=date>11/25/2004</div>
<div class=post>
<h3 class=xstorytitle id=post-73><a title="Permanent Link: osworkflow解读之四" href="http://morningspace.51.net/weblog/index.php?p=73" rel=bookmark><u><font color=#0000ff>osworkflow解读之四</font></u></a></h3>
<div class=meta>Filed under:
<ul class=post-categories>
    <li><a title="View all posts in Workflow" href="http://morningspace.51.net/weblog/index.php?cat=6"><font color=#0000ff size=2><u>Workflow</u></font></a> </li>
</ul>
— site admin @ 9:38 am </div>
<div class=storycontent>
<p>流程启动&gt;&gt;</p>
<p>前面分析流程流转的执行逻辑时，并没有讲到流程的启动。实际上，有了前面的基础，再加上对流程定义文件加载的过程有清晰认识之后，流程启动逻辑是很容易理解的。大致情况如下：<br>- 调用DefaultConfiguration的getWorkflow，传入流程名称，然后返回一个WorkflowDescriptor实例，流程定义文件的加载就是在这个时候完成的；<br>- 然后做一些准备工作，比如：获取WorkflowStore实例、准备好transientVars和propertySet等等；<br>- 调用WorkflowDescriptor的getInitialAction方法，获取initial action（如果有的话），注意此前的第一步中，流程定义文件已经成功加载；<br>- 调用transitionWorkflow，执行流程的流转（就是前面提到doAction执行逻辑时调用的那个重要的方法）；</p>
<p>在transitionWorkflow方法中，与流程启动后step间的流转稍有不同的是：当发现action为initial action时，将会把流程设置为activated状态，表示流程启动。就是下面这段代码：</p>
<p class=prettycode>if ((wf.getInitialAction(action.getId()) != null) &amp;&amp; (entry.getState() != WorkflowEntry.ACTIVATED)) {<br>　changeEntryState(entry.getId(), WorkflowEntry.ACTIVATED);<br>} </p>
<p>WorkflowStore和WorkflowEntry&gt;&gt;</p>
<p>前面提到过，在AbstractWorkflow中，包含流程流转关键逻辑的transitionWorkflow方法，会创建新的step。而这一创建工作是通过调用另一个member method实现的，也就是createNewCurrentStep。其执行逻辑大致如下：</p>
<p class=prettycode>int nextStep = theResult.getStep();<br>if (nextStep == -1) {<br>　if (currentStep != null) {<br>　　nextStep = currentStep.getStepId();<br>　}<br>}<br>&#8230;<br>if (currentStep != null) {<br>　store.markFinished(currentStep, actionId, new Date(), oldStatus, context.getCaller());<br>　store.moveToHistory(currentStep);<br>}<br>&#8230;<br>Step newStep = store.createCurrentStep(entry.getId(), nextStep, owner, startDate, dueDate, status, previousIds); </p>
<p>从这段代码中，我们首先可以看到，osworkflow可以支持step节点自身再次被&#8220;激活&#8221;的行为，也就是重复执行某个step。而另一方面，随后的创建工作则是调用了store.createCurrentStep。</p>
<p>这个store变量就是一个实现了WorkflowStore接口的实例变量，缺省是MemoryWorkflowStore。WorkflowStore除了创建step之外，还提供了一系列find和query方法。在其内部分别保存着step的历史记录（history steps）以及当前处于&#8220;激活&#8221;状态的step（current steps），以MemoryWorkflowStore为例，分别对应了两个HashMap，而JDBCWorkflowStore则利用数据库表来记录这些信息。</p>
<p>实际上，WorkflowStore可以同时保存多个流程的记录，这样就可以满足同时存在多个流程的情况。为此，osworkflow提供了一个WorkflowEntry接口用来描述流程信息，其中包含了流程名称、流程ID、当前状态等等。WorkflowEntry中定义了如下几个流程状态常量：</p>
<p class=prettycode>public static final int CREATED = 0; //创建<br>public static final int ACTIVATED = 1; // 激活<br>public static final int SUSPENDED = 2; // 挂起<br>public static final int KILLED = 3; // 异常终止<br>public static final int COMPLETED = 4; // 正常结束<br>public static final int UNKNOWN = -1; </p>
<p>在WorkflowStore提供的接口方法中有如下几个方法供维护WorkflowEntry使用：</p>
<p class=prettycode>public WorkflowEntry createEntry(String workflowName) throws StoreException;<br>public WorkflowEntry findEntry(long entryId) throws StoreException;<br>public void setEntryState(long entryId, int state) throws StoreException; </p>
<p>具体的方法实现，各个具现类有所不同。比如。在MemoryWorkflowStore中，是通过维护一个存储SimpleWorkflowEntry实例（实现了WorkflowEntry接口）的HashMap达到目的的。 </p>
</div>
<div class=feedback><a href="http://morningspace.51.net/weblog/index.php?p=73#comments"><font color=#667755><u>Comments (0)</u></font></a> </div>
<!--	<rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 	    xmlns:dc="http://purl.org/dc/elements/1.1/"	    xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">		<rdf:description rdf:about="http://morningspace.51.net/weblog/index.php?p=73"    dc:identifier="http://morningspace.51.net/weblog/index.php?p=73"    dc:title="osworkflow解读之四"    trackback:ping="http://morningspace.51.net/weblog/wp-trackback.php/73" /></rdf:rdf>	--></div>
<div class=date>11/24/2004</div>
<div class=post>
<h3 class=xstorytitle id=post-72><a title="Permanent Link: OSWorkflow解读之三" href="http://morningspace.51.net/weblog/index.php?p=72" rel=bookmark><u><font color=#0000ff>OSWorkflow解读之三</font></u></a></h3>
<div class=meta>Filed under:
<ul class=post-categories>
    <li><a title="View all posts in Workflow" href="http://morningspace.51.net/weblog/index.php?cat=6"><font color=#0000ff size=2><u>Workflow</u></font></a> </li>
</ul>
— site admin @ 9:48 am </div>
<div class=storycontent>
<p>初始配置加载及工作流定义文件加载&gt;&gt;</p>
<p>AbstractWorkflow首先会取得一个Configuration实例，缺省时为DefaultConfiguration（实现了Configuration接口），该实例负责系统配置的加载。AbstractWorkflow会调用其load方法，该方法内部会查找一个名为osworkflow.xml的配置文件，并对其解析。osworkflow.xml文件中一般会指定persistence class和factory class，比如：</p>
<p class=prettycode>&lt;osworkflow&gt;<br>　&lt;persistence class="com.opensymphony.workflow.spi.memory.MemoryWorkflowStore"/&gt;<br>　&lt;factory class="com.opensymphony.workflow.loader.XMLWorkflowFactory"&gt;<br>　　&lt;property key="resource&#8221; value="workflows.xml&#8221; /&gt;<br>　&lt;/factory&gt;<br>&lt;/osworkflow&gt; </p>
<p>load方法会动态加载这些class，还可以指定一些参数，在它们初始化的时候传入。在这里，我们指定了XMLWorkflowFactory，实际上还有其他种类的WorkflowFactory，比如JDBCWorkflowFactory、URLWorkflowFactory，它们均派生自AbstractWorkflowFactory，其共同职责是通过某种媒介加载流程定义。</p>
<p>以XMLWorkflowFactory为例，其相应实例在load方法内部完成初始化的过程中，将会查找一个名为workflows.xml的文件，可以在该文件中定义多个流程，每个流程指明其对应的xml定义文件。比如：</p>
<p class=prettycode>&lt;workflows&gt;<br>　&lt;workflow name="example&#8221; type="resource&#8221; location="example.xml"/&gt;<br>&lt;/workflows&gt; </p>
<p>在XMLWorkflowFactory内部维护了一个Map，保存着流程名称与其对应的文件路径（实际上是一个WorkflowConfig实例，不过这只是细节）。然后，DefaultConfiguration调用XMLWorkflowFactory.getWorkflow方法，并传入流程名称。</p>
<p>getWorkflow方法内部又会将流程加载的具体执行逻辑转交给一个名为WorkflowLoader的类（调用WorkflowLoader.load方法），由WorkflowLoader实现流程定义文件读取。不过，真正的xml文件解析是由WorkflowDescriptor类完成的。它将平面的xml流转化为osworkflow内部所使用的具有真正意义的对象。此类有很多get方法，在osworkflow的许多地方都将会用到这个类的get方法，以获取具体的对象，比如：getAction，getJoin，getStep等等。</p>
<p>最终，代表example.xml流程定义文件的WorkflowDescriptor实例将会被逐层返回，直至AbstractWorkflow。至此，流程定义文件加载完毕，整个初始化过程也就基本完成了。 </p>
</div>
<div class=feedback><a href="http://morningspace.51.net/weblog/index.php?p=72#comments"><font color=#667755><u>Comments (0)</u></font></a> </div>
<!--	<rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 	    xmlns:dc="http://purl.org/dc/elements/1.1/"	    xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">		<rdf:description rdf:about="http://morningspace.51.net/weblog/index.php?p=72"    dc:identifier="http://morningspace.51.net/weblog/index.php?p=72"    dc:title="OSWorkflow解读之三"    trackback:ping="http://morningspace.51.net/weblog/wp-trackback.php/72" /></rdf:rdf>	--></div>
<div class=date>11/23/2004</div>
<div class=post>
<h3 class=xstorytitle id=post-70><a title="Permanent Link: OSWorkflow解读之二" href="http://morningspace.51.net/weblog/index.php?p=70" rel=bookmark><u><font color=#0000ff>OSWorkflow解读之二</font></u></a></h3>
<div class=meta>Filed under:
<ul class=post-categories>
    <li><a title="View all posts in Workflow" href="http://morningspace.51.net/weblog/index.php?cat=6"><font color=#0000ff size=2><u>Workflow</u></font></a> </li>
</ul>
— site admin @ 9:09 am </div>
<div class=storycontent>
<p>多种方式定制逻辑&gt;&gt;</p>
<p>osworkflow的几个基本元件都具有很好的扩展性，它们分别是：condition，function，register，validator。以condition为例，我们可以为触发action的condition定义任意复杂的逻辑，而这种逻辑可以包含在一个java class中，也可以采用bsf，或者bean shell，还有local ejb和remote ejb。只要流程定义文件做相应配置即可，以java class和bean shell为例：</p>
<p class=prettycode>&lt;action id="1&#8243; name="Start Workflow"&gt;<br>　&lt;restrict-to&gt;<br>　　&lt;conditions type="AND"&gt;<br>　　　&lt;condition type="beanshell"&gt;<br>　　　　&lt;arg name="script"&gt;true&lt;/arg&gt;<br>　　　&lt;/condition&gt;<br>　　　&lt;condition type="class"&gt;<br>　　　　&lt;arg name="class.name"&gt;com.opensymphony.workflow.util.OSUserGroupCondition&lt;/arg&gt;<br>　　　　&lt;arg name="group"&gt;foos&lt;/arg&gt;<br>　　　&lt;/condition&gt;<br>　　&lt;/conditions&gt;<br>　&lt;/restrict-to&gt;<br>　&#8230;<br>&lt;/action&gt; </p>
<p>在osworkflow的许多地方都可以见到类似如下的代码（此处以function为例）：</p>
<pre class=prettycode>if ("remote-ejb".equals(type)) {  clazz = RemoteEJBFunctionProvider.class.getName();} else if ("local-ejb".equals(type)) {  clazz = LocalEJBFunctionProvider.class.getName();} else if ("jndi".equals(type)) {  clazz = JNDIFunctionProvider.class.getName();} else if ("bsf".equals(type)) {  clazz = BSFFunctionProvider.class.getName();} else if ("beanshell".equals(type)) {  clazz = BeanShellFunctionProvider.class.getName();} else {  clazz = (String) args.get(CLASS_NAME);}FunctionProvider provider = (FunctionProvider) loadObject(clazz);provider.execute(transientVars, args, ps);</pre>
<p>loadObject会动态加载相应的具现处理类（比如BSFFunctionProvider），并转换为基类类型（比如FunctionProvider），然后调用相应的执行逻辑（比如provider.execute）。这一模式屡试不爽。 </p>
</div>
<div class=feedback><a href="http://morningspace.51.net/weblog/index.php?p=70#comments"><font color=#667755><u>Comments (0)</u></font></a> </div>
<!--	<rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 	    xmlns:dc="http://purl.org/dc/elements/1.1/"	    xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">		<rdf:description rdf:about="http://morningspace.51.net/weblog/index.php?p=70"    dc:identifier="http://morningspace.51.net/weblog/index.php?p=70"    dc:title="OSWorkflow解读之二"    trackback:ping="http://morningspace.51.net/weblog/wp-trackback.php/70" /></rdf:rdf>	--></div>
<div class=date>11/22/2004</div>
<div class=post>
<h3 class=xstorytitle id=post-68><a title="Permanent Link: OSWorkflow解读之一" href="http://morningspace.51.net/weblog/index.php?p=68" rel=bookmark><u><font color=#800080>OSWorkflow解读之一</font></u></a></h3>
<div class=meta>Filed under:
<ul class=post-categories>
    <li><a title="View all posts in Workflow" href="http://morningspace.51.net/weblog/index.php?cat=6"><font color=#0000ff size=2><u>Workflow</u></font></a> </li>
</ul>
— site admin @ 9:35 am </div>
<div class=storycontent>
<p>AbstractWorkflow&gt;&gt;</p>
<p>osworkflow中有关工作流流转的所有核心代码都在AbstractWorkflow中，BasicWorkflow就是派生自它，不过这个BasicWorkflow基本上没做什么事情。也许我们还可以从AbstractWorkflow派生自己的Workflow类以加入扩展功能，大概这也算是osworkflow所体现的一种灵活性了，即：允许对工作流流转的执行逻辑进行修改。AbstractWorkflow实现了Workflow接口，该接口包含了有关工作流的核心方法，最重要的是doAction方法，AbstractWorkflow实现了该方法，后面会提及，其他还有一些getter和query method。</p>
<p>流程流转的执行逻辑&gt;&gt;</p>
<p>当流程执行到的某个step时，可能有一个或多个action可供用户选择执行。一旦确定执行某个action后，我们需要调用AbstractWorkflow.doAction，并传入流程id和action的id。以下是对doAction的执行逻辑的一个不太严紧的算法描述：</p>
<p>*　　*　　*</p>
<p>- 　根据流程id，获得所有当前的step，这种情况往往发生在有split的时候，此时会有多个step等待执行；<br>- 　根据传入的action的id，检查是否是global action；<br>- 　若不是global action，则遍历所有当前的step，对每个step的每个action调用isActionAvailable方法，检查该action是否可用（记住step和action是一对多的关系）；<br>- 　所谓可用是指，通过执行passesConditions，逐个检查action的condition：若是OR的关系，则有一个condition为真即为可用，AND关系则类推；<br>- 　若action可用，则调用transitionWorkflow，这是流程流转处理的关键部分；<br>　执行transitionWorkflow时：<br>　- 　首先获取当前step，存在有多个当前step的情况，比如split，此时获取首个isAvailableAction为真的step；<br>　- 　调用verifyInputs验证输入（如果action有validator的话）；<br>　- 　执行当前step的post function（因为该step即将结束）；<br>　- 　执行action的pre function；<br>　- 　判断当前step所属的result中的所有condition是否满足要求，判断方法类似action的condition；<br>　- 　一旦满足，则获取result的pre function和post function；<br>　- 　否则即是unconditional result，获取相应的pre function和post function；<br>　- 　在没有split和join的情况下<br>　　-　 会根据在result中指定的下一个step的id，创建一个新的step，作为当前的step；<br>　　- 　从current steps中移除原来的当前step，并添加到history steps中；<br>　　- 　如果新的step有pre function，则会马上执行；<br>　- 　执行result的post function；<br>　- 　执行action的post function；<br>　- 　若action是intial action，则将流程设置为activated状态；<br>　- 　若action是finish action，则将流程设置为completed状态，返回true；<br>　- 　寻找auto action，若有的话，则执行之，执行方法是调用doAction本身；<br>　- 　返回false；<br>- 　根据transitionWorkflow的返回值判断流程是否结束；<br>- 　若返回false，则调用checkImplicitFinish检查是否存在implicit finish，即：当前没有一个step的action可用时，就认为流程应该结束；</p>
<p>*　　*　　*</p>
<p>-　若存在split，则会创建多个新的step，并且在创建之前先执行split的pre function，在创建之后执行split的post function；<br>-　创建step的过程和上面描述的普通状况相同：维护好current steps和history steps，并执行新的step的pre function；</p>
<p>*　　*　　*</p>
<p>-　若存在join，先结束当前step，并将该step添加至history steps和join steps；<br>-　查找history steps，对每个已完成的step，查看是否在其result或unconditional result中有join一项，若有则加入join steps中；<br>-　检查join是否已经满足：可以使用Bean Shell，在xml定义文件的join节点中，通过引用一个名为&#8220;jn&#8221;的特殊变量来指定join的满足条件，jn记录了有关join的关键信息；<br>-　若条件满足，则执行join的pre function，维护好history steps，并创建下一个step，然后执行join的post function；</p>
<p>*　　*　　*</p>
<p>-　对于条件循环的情况，可以通过将result的某个action的下一个step指定为自身来加以实现，这只是在xml定义文件中做文章，流程执行逻辑无需做特殊处理； </p>
</div>
<div class=feedback><a href="http://morningspace.51.net/weblog/index.php?p=68#comments"><font color=#667755><u>Comments (1)</u></font></a> </div>
<div class=date>11/17/2004</div>
<div class=post>
<h3 class=xstorytitle id=post-66><a title="Permanent Link: OSWorkflow开篇" href="http://morningspace.51.net/weblog/index.php?p=66" rel=bookmark><font color=#000080>OSWorkflow开篇</font></a></h3>
<div class=meta>Filed under:
<ul class=post-categories>
    <li><a title="View all posts in Workflow" href="http://morningspace.51.net/weblog/index.php?cat=6"><font color=#000080 size=2>Workflow</font></a> </li>
</ul>
— site admin @ 11:04 pm </div>
<div class=storycontent>
<p>刚刚下载了OSWorkflow，这里摘录的是联机文档中的开篇部分，也就是OSWorkflow的核心概念了：</p>
<p>OSWorkflow is based heavily on the concept of the <strong>finite state machine</strong>. Each state is represented by the combination of a <strong>step ID</strong> and a <strong>status</strong>. A <strong>transition</strong> from one state to another cannot happen without an <strong>action</strong> occuring first. There are always at least one or more active states during the lifetime </p>
</div>
</div>
</div>
<img src ="http://www.blogjava.net/hrcdg/aggbug/133760.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hrcdg/" target="_blank">Eric huang</a> 2007-08-01 11:19 <a href="http://www.blogjava.net/hrcdg/articles/133760.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>