﻿<?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-j2ee绿洲-文章分类-OSWorkflow</title><link>http://www.blogjava.net/livery/category/23798.html</link><description>找到属于自己的一片天空</description><language>zh-cn</language><lastBuildDate>Sun, 08 Jul 2007 19:33:35 GMT</lastBuildDate><pubDate>Sun, 08 Jul 2007 19:33:35 GMT</pubDate><ttl>60</ttl><item><title>osworkflow api 之jdbc和hibernate</title><link>http://www.blogjava.net/livery/articles/127796.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Tue, 03 Jul 2007 03:48:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/127796.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/127796.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/127796.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/127796.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/127796.html</trackback:ping><description><![CDATA[<p><span><a title="class in com.opensymphony.workflow.spi.jdbc" href="file:///D:docsosworkflowapicomopensymphonyworkflowspijdbcJDBCWorkflowStore.html" target=classFrame><span><font face="Times New Roman" size=3>JDBCWorkflowStore</font></span></a></span><font size=3><span>（实现于</span><span><font face="Times New Roman">WorkflowStore</font></span><span>）</span><span><font face="Times New Roman"> </font></span><span>、</span><span><a title="class in com.opensymphony.workflow.spi.jdbc" href="file:///D:docsosworkflowapicomopensymphonyworkflowspijdbcMySQLWorkflowStore.html" target=classFrame><span><font face="Times New Roman">MySQLWorkflowStore</font></span></a><font face="Times New Roman">(</font></span><span>继承于</span><span><font face="Times New Roman">JDBCWorkflowStore) </font></span></font>
<p><font size=3><span>先讲讲</span><span><font face="Times New Roman">JDBCWorkflowStore</font></span><span>：</span><span> </span></font>
<p><font size=3><span>首先理解一下</span><span><font face="Times New Roman">osworkflow</font></span><span>的表结构，在你所下载的压缩包里有个</span><span><font face="Times New Roman">src\etc\deployment\jdbc</font></span><span>目录，在这个目录下面有常用的各种数据库的</span><span><font face="Times New Roman">ddl</font></span><span>。</span><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span>这是</span><span><font face="Times New Roman">osworkflow</font></span><span>内置的表结构。基本上就是这个类来对</span><span><font face="Times New Roman">osworkflow</font></span><span>的这个内置表进行实际操作的。</span><span> </span></font>
<p><font size=3><span>这个类里的方法众多，而且基本就是对数据库的基本操作的各种各样的方法，当然这里结合了工作流的知识，如针对</span><span><font face="Times New Roman">step</font></span><span>的某些有意义数据库操作，大致情况就是这样的。</span><span> </span></font>
<p><font size=3><span>如</span><span><font face="Times New Roman">query</font></span><span>方法的具体的</span><span><font face="Times New Roman">jdbc</font></span><span>方式的实现就在这里的。</span><span>&nbsp;</span></font>&nbsp;
<p><span><a title="class in com.opensymphony.workflow.spi.jdbc" href="file:///D:docsosworkflowapicomopensymphonyworkflowspijdbcMySQLWorkflowStore.html" target=classFrame><span><font face="Times New Roman" size=3>MySQLWorkflowStore</font></span></a></span><font size=3><span>继承自</span><span><font face="Times New Roman">JDBCWorkflowStore</font></span><span>，另外重写了两个方法</span><span><font face="Times New Roman">init</font></span><span>和</span><span><font face="Times New Roman">getNextStepSequence</font></span><span>。在这里可以写针对自己需要用到的</span><span><font face="Times New Roman">workflowstore</font></span><span>，只要继承自</span><span><font face="Times New Roman">JDBCWorkflow</font></span><span>，并写重写必要的方法满足自己的实际需求即可。</span><span> </span></font>
<p><font size=3><span>在我的上一篇</span><span><font face="Times New Roman">blog</font></span><span>中持久化</span><span><font face="Times New Roman">jdbcstore</font></span><span>中的</span><span><font face="Times New Roman">osworkflow.xml</font></span><span>中有相应的对</span><span><font face="Times New Roman">jdbcstore</font></span><span>的存储范例。</span><span> </span></font>
<p><span><a href="file:///D:ceosworkflowdocsapicomopensymphonyworkflowspihibernatepackage-frame.html" target=packageFrame><span><font face="Times New Roman" size=3>com.opensymphony.workflow.spi.hibernate</font></span></a>
<p></span>&nbsp;</p>
<p><span><font face="Times New Roman"><font size=3>** </font></font></span>
<p><font size=3><span>这个包里主要是</span><span><font face="Times New Roman">osworkflow</font></span><span>提供对</span><span><font face="Times New Roman">hibernate</font></span><span>支持。</span><span> </span></font>
<p><font size=3><span>首先来看一下</span><span><font face="Times New Roman">HibernateWorkflowStore</font></span><span>类。</span><span> </span></font>
<p><font size=3><span>首先来看一下类注释：</span><span> </span></font>
<p><span><font face="Times New Roman"><font size=3>/** </font></font></span>
<p><span><font face="Times New Roman"><font size=3>&nbsp;* A workflow store backed by Hibernate for persistence.&nbsp;To use this with the standard </font></font></span>
<p><span><font face="Times New Roman"><font size=3>&nbsp;* persistence factory, pass to the DefaultConfiguration.persistenceArgs the SessionFactory to </font></font></span>
<p><span><font face="Times New Roman"><font size=3>&nbsp;* use: </font></font></span>
<p><span><font face="Times New Roman"><font size=3>&nbsp;* &lt;code&gt;DefaultConfiguration.persistenceArgs.put("sessionFactory", DatabaseHelper.getSessionFactory());&lt;/code&gt; </font></font></span>
<p><span><font face="Times New Roman"><font size=3>&nbsp;* See the HibernateFunctionalWorkflowTestCase for more help. </font></font></span>
<p><span><font face="Times New Roman"><font size=3>&nbsp;*&nbsp;</font></font></span>&nbsp;
<p><span><font face="Times New Roman"><font size=3>&nbsp;* @author $Author: hani $ </font></font></span>
<p><span><font face="Times New Roman"><font size=3>&nbsp;* @version $Revision: 1.18 $ </font></font></span>
<p><span><font face="Times New Roman"><font size=3>&nbsp;*/ </font></font></span>
<p><font size=3><span>大致意思是说，以</span><span><font face="Times New Roman">hibernate</font></span><span>作为工作流的持久存储，为了使用标准的持久工厂即</span><span><font face="Times New Roman">sessionfactory</font></span><span>，在测试类</span><span><font face="Times New Roman">HibernateFunctionalWorkflowTestCase</font></span><span>里有较为完整的代码。其中借助的是</span><span><font face="Times New Roman">DefaultConfiguration.persistenceArgs.put("sessionFactory", DatabaseHelper.getSessionFactory())</font></span><span>。然后在</span><span><font face="Times New Roman">DefaultConfiguration</font></span><span>的</span><span><font face="Times New Roman">getWorkflowStore</font></span><span>获取</span><span><font face="Times New Roman">workflowstore</font></span><span>对象的时候，会调用</span><span><font face="Times New Roman">store.init(getPersistenceArgs());</font></span><span>实际执行的代码为：</span><span> </span></font>
<p><span><font face="Times New Roman"><font size=3>sessionFactory = (SessionFactory) props.get("sessionFactory"); </font></font></span>
<p><span><font face="Times New Roman"><font size=3>session = sessionFactory.openSession(); </font></font></span>
<p><font size=3><span>则可获得到</span><span><font face="Times New Roman">session</font></span><span>对象。</span><span> </span></font>
<p><font size=3><span>简单的可以理解为，</span><span><font face="Times New Roman">put</font></span><span>，然后</span><span><font face="Times New Roman">store</font></span><span>的</span><span><font face="Times New Roman">init</font></span><span>，最后</span><span><font face="Times New Roman">get</font></span><span>出就可以得到。而真正的创建</span><span><font face="Times New Roman">sessionfactory</font></span><span>是自己来完成，如上面是在</span><span><font face="Times New Roman">DatabaseHelper</font></span><span>类中的</span><span><font face="Times New Roman">createHibernateSessionFactory</font></span><span>方法创建出实际的</span><span><font face="Times New Roman">sessionfactory</font></span><span>。</span><span> </span></font>
<p><font size=3><span>从</span><span><font face="Times New Roman">osworkflow</font></span><span>自己带的测试类</span><span><font face="Times New Roman">HibernateFunctionalWorkflowTestCase</font></span><span>中的</span><span><font face="Times New Roman">setup</font></span><span>方法中可以看出</span><span> </span></font>
<p><span><font face="Times New Roman"><font size=3>super.setUp(); </font></font></span>
<p><span><font face="Times New Roman"><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>DatabaseHelper.createDatabase(""); </font></font></span>
<p><span><font face="Times New Roman"><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>factory = DatabaseHelper.createHibernateSessionFactory(); </font></font></span>
<p><span><font face="Times New Roman"><font size=3>&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Configuration config = new DefaultConfiguration(); </font></font></span>
<p><span><font face="Times New Roman"><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>config.load(getClass().getResource("/osworkflow-hibernate.xml")); </font></font></span>
<p><span><font face="Times New Roman"><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>workflow.setConfiguration(config); </font></font></span>
<p><span><font face="Times New Roman"><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>workflow.getConfiguration().getPersistenceArgs().put("sessionFactory", factory); </font></font></span>
<p><font size=3><span>核心的实现方法在</span><span><font face="Times New Roman">DatabaseHelper </font></span><span>类中的</span><span><font face="Times New Roman">createHibernateSessionFactory</font></span><span>方法。有兴趣的可以到这些测试代码中看一下具体是如何实现的。</span><span> </span></font>
<p><font size=3><span>其实大不可不必，如过你想要让</span><span><font face="Times New Roman">HibernateWorkflowStore</font></span><span>自己具有产生</span><span><font face="Times New Roman">sessionfactory</font></span><span>的功能，只要按照</span><span><font face="Times New Roman">hibernate</font></span><span>正常的生成办法写到</span><span><font face="Times New Roman">HibernateWorkflowStore</font></span><span>里就可以了。</span><span> </span></font>
<p><font size=3><span>我想如果要是想更方便的话可以通过在</span><span><font face="Times New Roman">HibernateWorkflowStore</font></span><span>内部生成</span><span><font face="Times New Roman">HibernateWorkflowStore</font></span><span>，既然</span><span><font face="Times New Roman">osworkflow</font></span><span>的作者不让在自己内部产生</span><span><font face="Times New Roman">sessionfactory</font></span><span>，她可能认为这个是用户自己的事，而不是</span><span><font face="Times New Roman">osworkflow</font></span><span>的事。那作为我们使用，我想可以更为方便点的修改它的源代码来达到这一目的。</span><span> </span></font>
<p><span><font face="Times New Roman"><font size=3>private static SessionFactory sessionFactory; </font></font></span>
<p><span><font face="Times New Roman"><font size=3>&nbsp;&nbsp;//~ Constructors ///////////////////////////////////////////////////////////<br>&nbsp;&nbsp;static{<br>&nbsp;&nbsp;&nbsp;try { <br>&nbsp;&nbsp;&nbsp;&nbsp; sessionFactory = new Configuration().configure().buildSessionFactory(); <br>&nbsp;&nbsp;&nbsp;} catch (HibernateException ex) { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new RuntimeException("Configuration problem: " + ex.getMessage(), ex); <br>&nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp;&nbsp;} </font></font></span>
<p><font size=3><span>二是在</span><span><font face="Times New Roman">init</font></span><span>中</span><span> </span></font>
<p><font size=3><span><font face="Times New Roman">&nbsp;&nbsp;</font></span><span>把</span><span><font face="Times New Roman">sessionFactory = (SessionFactory) props.get("sessionFactory");</font></span><span>注释</span><span> </span></font>
<p><font size=3><span><font face="Times New Roman">&nbsp;&nbsp;</font></span><span>编译</span><span><font face="Times New Roman">,</font></span><span>生成</span><span><font face="Times New Roman">class,</font></span><span>放到</span><span><font face="Times New Roman">osworkflow-2.7.0.jar,</font></span><span>取代原来的</span><span><font face="Times New Roman">HibernateWorkflowStore.class </font></span></font>
<p><span><font face="Times New Roman"><font size=3></font></font></span>
<p><font size=3><span>上面讲的都是</span><span><font face="Times New Roman">hibernate</font></span><span>的</span><span><font face="Times New Roman">sessionfactory</font></span><span>相关的东西。</span><span> </span></font>
<p><font size=3><span>其实每个方法如果需要解析的都会写出这么多内容的，而到底是应该掌握一个什么样的学习方法呢？我想我还是在思考。也许是看多了就眼明手亮了。</span><span> </span></font>
<p><font size=3><span>其实最让我觉得不爽的地方就是这个</span><span><font face="Times New Roman">osworkflow</font></span><span>的作者的代码里注释量基本为</span><span><font face="Times New Roman">0</font></span><span>。不知道是什么原因。</span><span> </span></font>
<p><font size=3><span><font face="Times New Roman">HibernateStep</font></span><span>和</span><span><font face="Times New Roman">HibernateHistoryStep</font></span><span>和</span><span><font face="Times New Roman">HibernateCurrentStep</font></span><span>、</span><span><font face="Times New Roman">HibernateWorkflowEntry</font></span><span>基本没什么可说的，就是一些普通的</span><span><font face="Times New Roman">get</font></span><span>和</span><span><font face="Times New Roman">set</font></span><span>方法。</span><span> </span></font>
<p><font size=3><span><font face="Times New Roman">SpringHibernateWorkflowStore</font></span><span>类：</span><span> </span></font></p>
<span>主要是通过</span><span>spring </span><span>提供的对</span><span>hibernate</span><span>的支持</span><span>getHibernateTemplate</span><span>来实现。</span> 
<img src ="http://www.blogjava.net/livery/aggbug/127796.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2007-07-03 11:48 <a href="http://www.blogjava.net/livery/articles/127796.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>osworkflow-将osworkflow+mysql持久化之二:jdbcstore</title><link>http://www.blogjava.net/livery/articles/127797.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Tue, 03 Jul 2007 03:48:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/127797.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/127797.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/127797.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/127797.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/127797.html</trackback:ping><description><![CDATA[OSWORKFLOW-将Osworkflow+MYSQL持久化之二:JDBCStore<br>/*<br>&nbsp;*Author: Meanson Wang <br>&nbsp;*Date: 2005-01-15<br>&nbsp;*Email: meansonw@hotmail.com<br>*/
<p>Osworkflow支持以下的持久化：MemoryStore (default), SerializableStore, JDBCStore, OfbizStore, and EJBStore. <br><br>【环境】<br>WIN2000<br>Osworkflow 2.7.0<br>Tomat 5.0.25<br>Mysql 4.1.7-nt + mysql-connector-java-3.0.15-ga-bin.jar</p>
<p>以下是使用MYSQL来进行持久化，支持文档是OSWF的手册的1.4 Persistence Options.html。里面建议用的是HypersonicSQL.</p>
<p>【步骤一：】建库</p>
<p>在MYSQL里建立一个DB命名为osworkflow<br>执行压缩文件src/etc/deployment/jdbc里面的mysql.sql建表。</p>
<p>【步骤二：】在TOMCAT里建立一个Datasource,命名为jdbc/oswf</p>
<p>1)Tomcat的admin中的DataSource中配置一个DataSource jdbc/oswf<br>2)$CATALINA_HOME/conf/catalina/localhost/$appname.xml中配置一个resource link</p>
<p>【步骤三：】修改osworkflow.xml</p>
<p>&lt;osworkflow&gt;<br>&lt;!--<br>&nbsp;&nbsp;&nbsp; &lt;persistence class="com.opensymphony.workflow.spi.memory.MemoryWorkflowStore"/&gt;<br>--&gt;<br>&lt;persistence class="com.opensymphony.workflow.spi.jdbc.MySQLWorkflowStore"&gt;</p>
<p>&nbsp;&lt;property key="datasource" value="jdbc/oswf"/&gt;<br>&nbsp;&lt;property key="entry.sequence" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value="SELECT max(id)+1 FROM OS_WFENTRY"/&gt;<br>&nbsp;&lt;property key="entry.table" value="OS_WFENTRY"/&gt;<br>&nbsp;&lt;property key="entry.id" value="ID"/&gt;<br>&nbsp;&lt;property key="entry.name" value="NAME"/&gt;<br>&nbsp;&lt;property key="entry.state" value="STATE"/&gt;<br>&nbsp;&lt;property key="step.sequence" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value="SELECT max(ID)+1 FROM OS_STEPIDS"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&lt;property key="step.sequence.increment" <br>&nbsp;&nbsp;&nbsp; &nbsp;value="INSERT INTO OS_STEPIDS (ID) values (null)"/&gt;<br>&nbsp; &nbsp;&lt;property key="step.sequence.retrieve" <br>&nbsp;&nbsp;&nbsp; &nbsp;value="SELECT max(ID) FROM OS_STEPIDS"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &nbsp;<br>&nbsp;&lt;property key="history.table" value="OS_HISTORYSTEP"/&gt;<br>&nbsp;&lt;property key="current.table" value="OS_CURRENTSTEP"/&gt;<br>&nbsp;&lt;property key="historyPrev.table" value="OS_HISTORYSTEP_PREV"/&gt;<br>&nbsp;&lt;property key="currentPrev.table" value="OS_CURRENTSTEP_PREV"/&gt;<br>&nbsp;&lt;property key="step.id" value="ID"/&gt;<br>&nbsp;&lt;property key="step.entryId" value="ENTRY_ID"/&gt;<br>&nbsp;&lt;property key="step.stepId" value="STEP_ID"/&gt;<br>&nbsp;&lt;property key="step.actionId" value="ACTION_ID"/&gt;<br>&nbsp;&lt;property key="step.owner" value="OWNER"/&gt;<br>&nbsp;&lt;property key="step.caller" value="CALLER"/&gt;<br>&nbsp;&lt;property key="step.startDate" value="START_DATE"/&gt;<br>&nbsp;&lt;property key="step.finishDate" value="FINISH_DATE"/&gt;<br>&nbsp;&lt;property key="step.dueDate" value="DUE_DATE"/&gt;<br>&nbsp;&lt;property key="step.status" value="STATUS"/&gt;<br>&nbsp;&lt;property key="step.previousId" value="PREVIOUS_ID"/&gt;<br>&lt;/persistence&gt;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; &lt;factory class="com.opensymphony.workflow.loader.XMLWorkflowFactory"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property key="resource" value="workflows.xml" /&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/factory&gt;<br>&lt;/osworkflow&gt;</p>
<p>【步骤四：】添加propertyset.xml文件到WEB-INF\classes下。</p>
<p>&lt;propertysets&gt;<br>&nbsp;&nbsp;&nbsp; &lt;propertyset name="jdbc" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="com.opensymphony.module.propertyset.database.JDBCPropertySet"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;arg name="datasource" value="jdbc/oswf"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;arg name="table.name" value="OS_PROPERTYENTRY"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;arg name="col.globalKey" value="GLOBAL_KEY"/&gt;<br></p>
<table>
    <tbody>
        <tr>
            <td></td>
        </tr>
    </tbody>
</table>
<script type=text/javascript><!--
google_ad_client = "pub-6625678643128649";
google_alternate_color = "FFFFFF";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
google_ad_channel ="";
//--></script>
<!--
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
-->
<img src ="http://www.blogjava.net/livery/aggbug/127797.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2007-07-03 11:48 <a href="http://www.blogjava.net/livery/articles/127797.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OSWorkflow源码2</title><link>http://www.blogjava.net/livery/articles/127794.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Tue, 03 Jul 2007 03:47:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/127794.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/127794.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/127794.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/127794.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/127794.html</trackback:ping><description><![CDATA[api-基本：<br>&nbsp;<br>sworkflow提供了集中工作流实现方式： <br>&nbsp;&nbsp; BasicWorkflow <br>&nbsp;&nbsp; EJBWorkflow <br>&nbsp;&nbsp; Ofbizworkflow <br>创建新的工作流实例，执行action <br>&nbsp;&nbsp; Workflow workflow = new BasicWorkflow("testuser"); <br>&nbsp;&nbsp; DefaultConfiguration config = new DefaultConfiguration(); <br>&nbsp;&nbsp; workflow.setConfiguration(config); <br>&nbsp;&nbsp; long workflowId = workflow.initialize("mytest", 1, null); <br>&nbsp;&nbsp; workflow.doAction(workflowId, 1, null); <br>api-abstractworkflow：<br>&nbsp;<br>osworkflow中有关工作流流转的所有核心代码都在AbstractWorkflow中，BasicWorkflow就是派生自它，可以从AbstractWorkflow派生自己的Workflow类以加入扩展功能 <br>最重要的方法doAction <br>其他主要功能：Initialize、executeFunction、众多get方法、query、state相关、等等 <br>api-配置文件相关：<br>&nbsp;<br>Configuration实例负责系统配置的加载。AbstractWorkflow会调用其load方法，该方法内部会查找一个名为osworkflow.xml的配置文件，并对其解析。 <br>WorkflowFactory包括XMLWorkflowFactory 、JDBCWorkflowFactory、URLWorkflowFactory,作用即是加载各个不同的工作流定义，维护一个map。 <br>WorkflowLoader的作用实现配置文件的读取 <br>WorkflowDescriptor的作用将平面的xml流转化为osworkflow内部所使用的具有真正意义的对象。 <br>其他不同的descriptor，如（step、action&#8230;&#8230;&#8230;&#8230;）之间的关系。 <br>其他不同的descriptor，如（step、action&#8230;&#8230;&#8230;&#8230;）之间的关系。 <br>api-查询：<br>&nbsp;<br>目的：希望了解流程当前的运行状况&#224;查询 <br>WorkflowQuery及其相关类（query包）&nbsp;<br>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>} <br>AbstractWorkflow导向workflowstore进行实际查询，最后将查询结果存储与arraylist中 <br>目的：希望了解流程当前的运行状况&#224;查询 <br>WorkflowQuery及其相关类（query包）&nbsp;<br>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>} <br>AbstractWorkflow导向workflowstore进行实际查询，最后将查询结果存储与arraylist中 <br>api-用户管理：<br>&nbsp;<br>OSWorkflow在用户管理方面所提供的功能，主要包括用户的创建、群组的定义、用户验证、以及对step执行人的跟踪记录和执行权限的判断等等 <br>用户/群组的管理是由UserManager来完成的 <br>代码中详细讲解 <br>代码中详细讲解 <br>osworkflow任务管理：<br>&nbsp;<br>OSWorkflow引擎只负责了&#8220;流程的运转&#8221;，当然这个运转会根据你所定义的Action和condtion来判断。 <br>Condition—条件判断 <br>Function（pre and post）--Step、action、result执行过程需要调用的功能 <br>FunctionProvider接口、execute方法 <br>&lt;step id="4" name="Assign"&gt;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;pre-functions&gt;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;function type="class"&gt;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;arg name="class.name"&gt;nucleus.assign.AssignmentFunction&lt;/arg&gt;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;arg name="Participant"&gt;A&lt;/arg&gt;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;arg name="ParticipantType"&gt;role&lt;/arg&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;arg name="actionID"&gt;22&lt;/arg&gt;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/function&gt;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/pre-functions&gt;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;actions&gt;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#183;&#183;&#183;&#183;&#183;&#183;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/actions&gt;&nbsp;<br>&nbsp;&nbsp;&nbsp; &lt;/step&gt; <br>&nbsp;&nbsp;&nbsp; &lt;/step&gt; <br>osworkflow的schedule：<br>&nbsp;<br>定时执行某项任务的功能，Quartz <br>&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; <br>&#167;Trigger和jobDetail，trigger触发条件满足后，则会激活真正的job实例，job实例真正执行的是trigger function（在配置文件中定义） <br>最后讲解osworkflow自带小例子，约1小时40分钟讲完。以上为ppt 的基本内容。
<img src ="http://www.blogjava.net/livery/aggbug/127794.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2007-07-03 11:47 <a href="http://www.blogjava.net/livery/articles/127794.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OSWorkflow概述</title><link>http://www.blogjava.net/livery/articles/127791.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Tue, 03 Jul 2007 03:44:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/127791.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/127791.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/127791.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/127791.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/127791.html</trackback:ping><description><![CDATA[<p><font size=3><span><font face="Times New Roman">Osworkflow</font></span><span>与目前绝大多书的工作流系统是不同的，而最大的不同点体现在它的韧性上和灵活程度上，在商业界和开源世界都存在它的影子。最开始大家可能比较难于理解，举个例子：</span><span><font face="Times New Roman">osworkflow</font></span><span>并不强制要求您用图形工具来开发工作流，推荐的首选办法是手写</span><span><font face="Times New Roman">xml</font></span><span>文件（即手写过程定义的</span><span><font face="Times New Roman">xml</font></span><span>文档，而图形工具操作的实质也是操作此</span><span><font face="Times New Roman">xml</font></span><span>，图形工具只是给非专业人士如业务分析人员，过程定义人员使用的）。它充分胜任这种整合，就想现存代码和数据库之间整合一样。虽然这样似乎看起来并不太适合进行快速所谓的&#8220;即插即用&#8221;工作流解决方案，但是</span><span><font face="Times New Roman">osworkflow</font></span><span>所提供的解决方案能够提供足够的灵活度来满足一个大型各种应用的所有需求。</span></font></p>
<p><font size=3><strong><span><font face="Times New Roman">Osworkflow</font></span></strong><strong><span>的韧性：</span><span> </span></strong></font>
<p><font size=3><span>可以把</span><span><font face="Times New Roman">osworkflow</font></span><span>看做一个低层次的工作流实现。在其他工作流系统中像</span><span><font face="Times New Roman">loops</font></span><span>和</span><span><font face="Times New Roman">conditions</font></span><span>这样的情况可以以图形图标形式展现出来，在</span><span><font face="Times New Roman">osworkflow</font></span><span>中必须进行编码。就是说最起码的脚本语言必须来如此设定。所以并不希望非技术人员来修改工作流。尽管一些系统提供了</span><span><font face="Times New Roman">GUI</font></span><span>操作来完成简单的工作流编辑，但是这种做法并不是十全十美的，如当这样改变流程后，此工作流周边的应用往往被破坏。所以</span><span><font face="Times New Roman">osworkflow</font></span><span>始终认为最好的变更控制办法就是以开发人员（前提：熟知每个变化）来做这些操作。</span><span><font face="Times New Roman">Osworkflow</font></span><span>在</span><chsdate isrocdate="False" islunardate="False" day="30" month="12" year="1899" w:st="on"></chsdate><span><font face="Times New Roman">2.5.0</font></span><span>版本就开始支持图形操作（</span><span><font face="Times New Roman">designer</font></span><span>），</span><span><font face="Times New Roman">2.6.0</font></span><span>和</span><span><font face="Times New Roman">2.7.0</font></span><span>做了许多改进。打算在</span><span><font face="Times New Roman">3.0</font></span><span>中正式推出。所以现在在使用</span><span><font face="Times New Roman">GUI</font></span><span>操作时候还是需要进行一些适当的预防手段的。</span></font></p>
<p><font size=3><span><font face="Times New Roman">Osworkflow</font></span><span>内部使用的是有限状态机的概念（对于这部分理解不好的，可以参看</span><span><font face="Times New Roman">UML</font></span><span>中的状态图部分，就可以有个初步理解）。每个状态都是通过一个</span><span><font face="Times New Roman">step ID</font></span><span>和一个状态来共同描述的。一个变迁（从一个状态到另外一个状态）只有在一个</span><span><font face="Times New Roman">action</font></span><span>发生的前提下才会被触发。在一个工作流的生命周期中始终会有一个或多个活动的状态。简单的讲就是以工作流引擎和简单</span><span><font face="Times New Roman">xml</font></span><span>为基础来完成商务工作流过程处理。</span></font></p>
<p><strong><font size=3><span>先决知识准备部分：</span><span> </span></font></strong>
<p><font size=3><span><font face="Times New Roman">Osworkflow</font></span><span>包括：</span></font></p>
<p><span><font face="Times New Roman" size=3>Oscore <chsdate isrocdate="False" islunardate="False" day="30" month="12" year="1899" w:st="on"></chsdate>2.0.1+</font></span></p>
<p><span><font face="Times New Roman" size=3>Propertryset 1.2+</font></span></p>
<p><font size=3><font face="Times New Roman"><city w:st="on"></city>
<place w:st="on"></place>
<span>Jakarta</span><span> commons-logging</span></font></font></p>
<p><font size=3><span><font face="Times New Roman">Beanshell</font></span><span>（可选）</span></font></p>
<p><font size=3><span><font face="Times New Roman">BSF</font></span><span>（可选）</span></font></p>
<p><font size=3><span><font face="Times New Roman">EJB</font></span><span>接口（并不是一定要在</span><span><font face="Times New Roman">EJB container</font></span><span>中）</span></font></p>
<p><font size=3><span><font face="Times New Roman">Xml</font></span><span>解析（在</span><span><font face="Times New Roman">jdk1.4</font></span><span>中并不是必须的）</span></font></p>
<p><font size=3><span><font face="Times New Roman">Osworkflow </font></span><span>的</span><span><font face="Times New Roman">api</font></span><span>部分将会支持</span><span><font face="Times New Roman">jdk1.3+</font></span><span>。</span><span><font face="Times New Roman">GUI designer</font></span><span>部分需要</span><span><font face="Times New Roman">1.4</font></span><span>的</span><span><font face="Times New Roman">JVM</font></span><span>。</span></font></p>
<p><font size=3><span>关于</span><span><font face="Times New Roman">soap</font></span><span>和工作调度：</span><span><font face="Times New Roman">GLUE</font></span><span>部分（我还没接触过，不敢乱讲，大家看原英文解释部分吧。）大致意思是说如果需要</span><span><font face="Times New Roman">soap</font></span><span>或者远程作业调度支持的，就需要下载</span><span><font face="Times New Roman">GLUE</font></span><span>专业库。另外还有就是</span><span><font face="Times New Roman">Quartz</font></span><span>来支持。如果在应用服务器上</span><span><font face="Times New Roman">run Quartz</font></span><span>，那么就经过适当配置就不再需要</span><span><font face="Times New Roman">GLUE</font></span><span>了，但是必须把作业调度的</span><span><font face="Times New Roman">local</font></span><span>参数设置为</span><span><font face="Times New Roman">true</font></span><span>。</span></font></p>
<p><strong><font size=3><span>运行例子：</span></font></strong>
<p><font size=3><span>简单的部署没有什么特别说。仅仅把</span><span><font face="Times New Roman">osworkflow</font></span><span>所带的例子拷贝到</span><span><font face="Times New Roman">tomcat</font></span><span>（举例）的</span><span><font face="Times New Roman">webapps</font></span><span>下就可以了。所带的这个例子不需要配置什么</span><span><font face="Times New Roman">DB</font></span><span>，当然也就做不了持久性处理（即所有的所有都没存储到实际数据库中。）当然这并不是实际应用当中所想见到的，这个例子的意义就在于简单的理解</span><span><font face="Times New Roman">osworkflow</font></span><span>。</span></font></p>
<p><font size=3><span>对于进行持久数据存储的例子在英文原件中有，我就不复制粘贴过来了。大家自己去看就可以了。具体都是如何修改配置文件，如果</span><span><font face="Times New Roman">tomcat</font></span><span>的</span><span><font face="Times New Roman">server.xml</font></span><span>文件，以及必要的</span><span><font face="Times New Roman">jar</font></span><span>拷贝工作。这些实际应用的例子会在后继文档中也许会有阐述，如果特别简单就不多写这方面东西了。感觉上不会复杂。呵呵</span><span><font face="Times New Roman">^_^</font></span></font></p>
<p><font size=3><strong><span>持久存储</span></strong><span>：</span></font></p>
<p><font size=3><span><font face="Times New Roman">Osworkflow</font></span><span>共提供了五种存储机制：</span><span><font face="Times New Roman">memorystore</font></span><span>（默认）自带的例子就是如此，</span><span><font face="Times New Roman">SerializableStore,JDBCStore,ofbizstore,</font></span><span>和</span><span><font face="Times New Roman">EJBStore.</font></span><span>如果上述这些都没满足您的要求，那就得自己来实现</span><span><font face="Times New Roman">osworkflow</font></span><span>的接口</span><span><font face="Times New Roman">com.opensymphony.workflow.spi.workflows</font></span><span>了。具体看</span><span><font face="Times New Roman">api</font></span><span>吧。本文只是入门篇，大致知道有这么一回事的。</span></font></p>
<p><font size=3><span>不同的存储实现需要不同的配置设置工作。推荐大家看</span><span><font face="Times New Roman">api</font></span><span>（此部分）来完成配置。原文中给出了</span><span><font face="Times New Roman">JDBC</font></span><span>的配置例子：</span></font></p>
<p><span><font size=3>请参看原文。</font></span></p>
<p><strong><font size=3><span>工作流定义部分：</span><span> </span></font></strong>
<p><font size=3><strong><span><font face="Times New Roman">Osworkflow </font></span></strong><span>尽最大可能使用配置来保证灵活度。仅一个文件</span><span><font face="Times New Roman">osworkflow.xml</font></span><span>需要在</span><span><font face="Times New Roman">classpath</font></span><span>中。这个文件设置了持久存储的方法（</span><span><font face="Times New Roman">jdbc,ejb,ofbiz</font></span><span>），工作流工厂类被用来加载工作流定义内容。</span></font></p>
<p><font size=3><span>默认的工厂是：</span><span><font face="Times New Roman">com.opensymphony.workflow.loader.xmlworkflowfactory</font></span><span>。从</span><span><font face="Times New Roman">classpath</font></span><span>中加载文件。</span></font></p>
<p><font size=3><span>在测试的时候，更改工作流定义文件然后重新加载它，你可以通过一个</span><span><font face="Times New Roman">reload</font></span><span>参数来指定是否完成自动重载。如果想指定自己的工作流定义，需要继承</span><span><font face="Times New Roman">com.opensymphony.workflow.loader.Abstractworkflowfactory</font></span><span>。然后剩下的就是你自己的任务了。</span></font></p>
<p><font size=3><strong><span><font face="Times New Roman">com.opensymphony.workflow.loader.JDBCWorkflowFactory</font></span></strong><span>是一个选择工厂，他允许你存储工作流定义为数据库内容而不是在</span><span><font face="Times New Roman">xml</font></span><span>文件中。</span><span> </span></font>
<p><font size=3><span>一般配置如下：<br>
<table style="WIDTH: 320px; BORDER-COLLAPSE: collapse" cellSpacing=0 cellPadding=3 border=1>
    <tbody>
        <tr>
            <td>
            <h3><a name=1.5+Loading+Workflow+Definitions-osworkf></a><span><font face=宋体>osworkflow.xml:</font></span></h3>
            <pre><span><font face=宋体 size=3>&lt;osworkflow&gt;</font></span></pre>
            <pre><span><font size=3><font face=宋体>&nbsp;&lt;persistence class="com.opensymphony.workflow.spi.jdbc.JDBCWorkflowStore"&gt;</font></font></span></pre>
            <pre><span><font size=3><font face=宋体><span>&nbsp;&nbsp;&nbsp; </span>&lt;arg name="foo" value="bar"/&gt;</font></font></span></pre>
            <pre><span><font size=3><font face=宋体><span>&nbsp;&nbsp;&nbsp; </span>...</font></font></span></pre>
            <pre><span><font size=3><font face=宋体>&nbsp;&lt;/persistence&gt;</font></font></span></pre>
            <pre><span><font size=3><font face=宋体>&nbsp;&lt;factory class="com.opensymphony.workflow.loader.XMLWorkflowFactory"&gt;</font></font></span></pre>
            <pre><span><font size=3><font face=宋体><span>&nbsp;&nbsp;&nbsp; </span>&lt;property key="resource" value="workflows.xml" /&gt;</font></font></span></pre>
            <pre><span><font size=3><font face=宋体>&nbsp;&lt;/factory&gt;</font></font></span></pre>
            <pre><span><font face=宋体 size=3>&lt;/osworkflow&gt;</font></span></pre>
            </td>
        </tr>
        <tr>
            <td>
            <h3><a name=1.5+Loading+Workflow+Definitions-workflo></a><span><font face=宋体>workflows.xml:</font></span></h3>
            <pre><span><font face=宋体 size=3>&lt;workflows&gt;</font></span></pre>
            <pre><span><font size=3><font face=宋体>&nbsp;&lt;workflow name="example" type="resource" location="example.xml"/&gt;</font></font></span></pre>
            <pre><span><font face=宋体 size=3>&lt;/workflows&gt;</font></span></pre>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p><font size=3><span>理解</span><span>osworkflow.xml</span><span>：</span><span> </span></font>
<p><font size=3><span>加载</span><span>osworkflow.xml</span><span>过程</span></font><font size=3><span>:<br>1)DefaultConfiguration</span><span>中</span><span>,</span><span>在</span><span>load</span><span>方法中调用</span><span>getInputStream()</span><span>以获取系统的</span><span>osworkflow.xml</span><span>文件</span><span> </span></font>
<p><font size=3><span>在</span><span>getInputStream</span><span>方法中：</span><span> </span></font>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp; </span>protected InputStream getInputStream(URL url) { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>InputStream is = null; </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (url != null) { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>try { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>is = url.openStream(); </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} catch (Exception ex) { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (is == null) { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>try { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>is = classLoader.getResourceAsStream("osworkflow.xml"); </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} catch (Exception e) { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (is == null) { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>try { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>is = classLoader.getResourceAsStream("/osworkflow.xml"); </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} catch (Exception e) { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (is == null) { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>try { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>is = classLoader.getResourceAsStream("META-INF/osworkflow.xml"); </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} catch (Exception e) { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (is == null) { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>try { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>is = classLoader.getResourceAsStream("/META-INF/osworkflow.xml"); </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} catch (Exception e) { </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>} </font></span>
<p><span><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return is;&nbsp;</font></span>&nbsp;
<p><span><font size=3>} </font></span>
<p><font size=3><span>在</span><span>load</span><span>方法中</span><span> </span></font>
<p><font size=3><span>Document doc = db.parse(is); //</span><span>开始解析由上面方法传递回来的流</span><span> </span></font>
<p><font size=3><span>然后可以看到接下来通过</span><span>xml</span><span>中指定的工厂类进行</span><span>init </span></font>
<p><font size=3>&nbsp;<span>由上面代码可以看的出</span><span>defaultconfiguration</span><span>是如何加载</span><span>osworkflow.xml</span><span>的。</span><span> </span></font>
<p><font size=3><span>load()</span><span>方法解析文件</span><span>,</span><span>并</span><span>load</span><span>相应的工厂类</span><span>. </span></font>
<p><font size=3><span>workflowFactory</span><span>：</span><span> </span></font>
<p><font size=3><span>在</span><span><font face="Times New Roman">loader</font></span><span>包下提供了多种多种格式的</span><span><font face="Times New Roman">factory</font></span><span>，如</span><span><font face="Times New Roman">xmlworkflowfactory</font></span><span>、</span><span><font face="Times New Roman">JDBCworkflowfactory</font></span><span>、</span><span><font face="Times New Roman">URLworkflowfactory</font></span><span>、</span><span><font face="Times New Roman">springworkflowfactory</font></span><span>（在实际中</span><span><font face="Times New Roman">jar</font></span><span>包中没找到，但是在</span><span><font face="Times New Roman">javadoc</font></span><span>中确实有）等等。</span></font></p>
<p><font size=3><span>这几种工厂类都是在各自的</span><span><font face="Times New Roman">getWorkflow</font></span><span>方法体内调用</span><span><font face="Times New Roman">loadWorkflow</font></span><span>方法。</span></font></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>private void loadWorkflow(WorkflowConfig c) throws FactoryException {</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>try {</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>c.descriptor = WorkflowLoader.load(c.url);</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>} catch (Exception e) {</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>throw new FactoryException("Error in workflow descriptor: " + c.url, e);</font></font></span></p>
<p><span><font size=3><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p><span><font face="Times New Roman" size=3>}</font></span></p>
<p><font size=3><span>而实际调用的则是</span><span><font face="Times New Roman">workflowloader.load</font></span><span>方法。可以进一步跟踪到</span><span><font face="Times New Roman">workflowloader</font></span><span>中，可以看到如何是将</span><span><font face="Times New Roman">descriptor</font></span><span>返回的。</span></font></p>
<p><font size=3><span>com.opensymphony.workflow.spi &nbsp;spi</span><span>指</span></font><font size=3><span>server provider identification<br></span><span>在</span><span>spi</span><span>包中可以看到各种存储的子包</span><span> </span></font>
<p><span>
<p><font size=3>~~在load方法中将persistenceArgs参数返回会在上面这些包的类中进行处理&#8230;&#8230;</font></p>
<p></span><br></span></font></p>
<img src ="http://www.blogjava.net/livery/aggbug/127791.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2007-07-03 11:44 <a href="http://www.blogjava.net/livery/articles/127791.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>osworkflow--function与workitem</title><link>http://www.blogjava.net/livery/articles/127790.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Tue, 03 Jul 2007 03:37:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/127790.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/127790.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/127790.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/127790.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/127790.html</trackback:ping><description><![CDATA[如何利用OSWorkflow的function进行任务的分配：<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OSWorkflow<span>是只是一个</span><span>workflow engine</span><span>的内核体。我们都说</span><span>osworkflow</span><span>非常的易扩展，但是这也同样说明了，用</span><span>osworkflow</span><span>去实现一个能够运行的工作流系统是非常<span>繁琐</span>的事情。繁琐并不是难，因为你要想实现一个流程，不得不自己去实现大量的</span><span>condition</span><span>和</span><span>function</span><span>。</span>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>既然说到工作流，那么肯定会涉及到&#8220;任务交给谁做&#8221;的问题。但是</span><span>OSWorkflow</span><span>压根就没有管这种需求，对于其来说，其提供了</span><span>c</span><span>和</span><span>f</span><span>，如果再有什么额外的需求和功能，那么就扩展</span><span>condition</span><span>或</span><span>function</span><span>。于是，你不得不扩展一些</span><span>function</span><span>类去处理&#8220;角色&#8221;&#8220;任务分配&#8221;&#8220;提交任务&#8221;等等诸如此类的操作。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>在我的标题中提到了</span><span>workitem</span><span>，这个概念几乎在其他工作流引擎都有所体现，但是对</span><span>osworkflow</span><span>来说，这是一个空白区域。至于</span><span>workitem</span><span>的含义，请参考</span><span>wfmc</span><span>的《</span><span><a href="http://www.wfmc.org/standards/model.htm" target=_blank>Terminology &amp; Glossary</a></span><span>》。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>OSWorkflow</span><span>引擎只负责了&#8220;流程的运转&#8221;，当然这个运转会根据你所定义的</span><span>Action</span><span>和</span><span>condtion</span><span>来判断。</span><span>Funtion</span><span>对</span><span>osworkflow</span><span>来说，只是</span><span>step</span><span>、</span><span>action</span><span>、</span><span>result</span><span>执行过程需要调用的功能，至于这个功能作什么，</span><span>OSWorkflow</span><span>并不关心，引擎只是负责提供几个参数接口。</span></p>
<p>&nbsp;</p>
<table cellSpacing=0 cellPadding=0 width=564 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=564>
            <p><span>public interface FunctionProvider {</span></p>
            <p><span>&nbsp;public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException;<br>}</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>所有的</span><span>Function</span><span>实现类都必须实现这个</span><span>FunctionProvider</span><span>接口中</span><span>execute</span><span>方法，而且能够处理的信息，也全部来自这个方法中的三个参数：</span></p>
<p>&nbsp;</p>
<table cellSpacing=0 cellPadding=0 width=569 align=left border=1>
    <tbody>
        <tr>
            <td vAlign=top width=115>
            <p><span>transientVars</span></p>
            </td>
            <td vAlign=top width=454>
            <p><span>这个是最为核心的参数，记录非常重要的一些对象，比如</span><span>WorkflowContext</span><span>，</span><span>WorkflowEntry</span><span>，输入参数等等。</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=115>
            <p><span>args</span></p>
            </td>
            <td vAlign=top width=454>
            <p><span>这个是</span><span>function</span><span>配置中的</span><span>arg</span><span>参数，具体请参考</span><span>osworkflow dtd</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=115>
            <p><span>ps</span></p>
            </td>
            <td vAlign=top width=454>
            <p><span>是</span><span>PropertySet</span><span>对象，记录了流程实例所需要保存的数据，可以理解成</span><span>osworkflow</span><span>所描述的流程相关数据。</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br><br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</span></span><span>具体</span><span>transienVars</span><span>中包含哪些对象，请参考</span> <span><a href="http://www.opensymphony.com/osworkflow/api/com/opensymphony/workflow/FunctionProvider.html" target=_blank>FunctionProvider api doc</a></span><span>。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>下面就说说如何利用</span><span>Function</span><span>进行任务的分配。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>个人建议你在</span><span>Step</span><span>的</span><span>pre-function</span><span>中做处理，配置如下：</span></p>
<table cellSpacing=0 cellPadding=0 width=492 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=492>
            <p><span><span>&nbsp;&nbsp;&nbsp; </span>&lt;step id="4" name="Assign"&gt;</span></p>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;pre-functions&gt;</span></p>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;function type="class"&gt;</span></p>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="class.name"&gt;nucleus.assign.AssignmentFunction&lt;/arg&gt;</span></p>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="<span>Participant</span>"&gt;A&lt;/arg&gt;</span></p>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="<span>ParticipantType</span>"&gt;role&lt;/arg&gt;<br><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&lt;arg name="actionID"&gt;22&lt;/arg&gt;</span></span></p>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;/function&gt;</span></p>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;/pre-functions&gt;</span></p>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;actions&gt;</span></p>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>&#183;&#183;&#183;&#183;&#183;&#183;</span></p>
            <p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;/actions&gt;</span></p>
            <p><span><span>&nbsp;&nbsp;&nbsp; </span>&lt;/step&gt;</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p><span>看了这个配置形式，我想大家应该明白如何去处理。你可以在</span><span>function</span><span>中获取自己所定义的角色、根据角色获取人员、根据人员产生</span><span>workitem</span><span>&#183;&#183;&#183;&#183;&#183;&#183;</span> <span>。你在</span><span>function </span><span>所作的这一切操作对</span><span>osworkflow engine</span><span>来说都是透明的——</span> <span>你所产生的</span><span>worklist</span><span>所代表的含义只有你自己知道。</span></p>
<p><span>其中我为什么会附加了一个</span><span>arg</span><span>属性：</span><span>actionID</span><span>？这是因为我需要告诉每一个</span><span>workitem</span><span>在其应该处理哪一个动作。</span> <span>因为外部程序都是通过</span><span><a href="http://www.opensymphony.com/osworkflow/api/com/opensymphony/workflow/Workflow.html#doAction(long, int, java.util.Map)"><code><span><font face=宋体 color=#164a7d>Workflow.doAction(long, int, java.util.Map)</font></span></code></a></span><span> </span><span>这个接口来激活流程的运转或改变实例的状态。</span></p>
<p>&nbsp;</p>
<p><span>总体来说，利用</span><span>osworkflow</span><span>去实现一个完整的工作流例子，还是比较麻烦的。主要是要扩展和自己实现的太多。</span></p>
<img src ="http://www.blogjava.net/livery/aggbug/127790.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2007-07-03 11:37 <a href="http://www.blogjava.net/livery/articles/127790.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OSWorker--descriptor 解析</title><link>http://www.blogjava.net/livery/articles/127788.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Tue, 03 Jul 2007 03:32:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/127788.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/127788.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/127788.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/127788.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/127788.html</trackback:ping><description><![CDATA[<p><font size=3><span>在这篇文章中，主要介绍</span><span><font face="Times New Roman">osworkflow</font></span><span>的核心概念以及重要的部分，让大家对</span><span><font face="Times New Roman">osworkflow</font></span><span>有个比较全面的认识。</span></font></p>
<p><font size=3><span>在</span><span><font face="Times New Roman">osworkflow</font></span><span>中最最核心的东西就是工作流定义的</span><span><font face="Times New Roman">xml</font></span><span>文件。尽管它并不是一定要定义成</span><span><font face="Times New Roman">xml</font></span><span>文件。但是</span><span><font face="Times New Roman">xml</font></span><span>格式是一种标准的通用的格式。</span></font></p>
<p><font size=3><span>这个</span><span><font face="Times New Roman">xml</font></span><span>文件为某一个给定的工作流进行描述</span><span><font face="Times New Roman">steps</font></span><span>、</span><span><font face="Times New Roman">states</font></span><span>，</span><span><font face="Times New Roman">transitions</font></span><span>，和</span><span><font face="Times New Roman">functionality</font></span><span>。下面阐述一下此</span><span><font face="Times New Roman">xml</font></span><span>的一般规则：</span></font></p>
<p><span><font face="Times New Roman"><font size=3>1、</font>&nbsp;</font></span><font size=3><span>一个工作流由多个</span><span><font face="Times New Roman">steps</font></span><span>组成</span></font></p>
<p><span><font face="Times New Roman"><font size=3>2、</font>&nbsp;</font></span><font size=3><span>对于每个</span><span><font face="Times New Roman">step</font></span><span>，可以包括多个</span><span><font face="Times New Roman">actions</font></span><span>。一个</span><span><font face="Times New Roman">action</font></span><span>可以被设置成自动运行或者需要通过人工交互才可以运行。</span></font></p>
<p><span><font face="Times New Roman"><font size=3>3、</font>&nbsp;</font></span><font size=3><span>每个</span><span><font face="Times New Roman">action</font></span><span>都要包括至少一个</span><span><font face="Times New Roman">unconditional result</font></span><span>和</span><span><font face="Times New Roman">0</font></span><span>或多个</span><span><font face="Times New Roman">conditional results</font></span><span>。</span></font></p>
<p><span><font face="Times New Roman"><font size=3>4、</font>&nbsp;</font></span><font size=3><span>如果设定了多个</span><span><font face="Times New Roman">concitioanl results</font></span><span>，所有当中的第一个将被执行，如果没有设定</span><span><font face="Times New Roman">conditional results</font></span><span>或者没有</span><span><font face="Times New Roman">conditions</font></span><span>满足，那么执行</span><span><font face="Times New Roman">unconditional result</font></span></font></p>
<p><span><font face="Times New Roman"><font size=3>5、</font>&nbsp;</font></span><font size=3><span>一个</span><span><font face="Times New Roman">result</font></span><span>过后可能依旧停留在当前的</span><span><font face="Times New Roman">step</font></span><span>中，一个新的</span><span><font face="Times New Roman">step</font></span><span>，一个</span><span><font face="Times New Roman">split</font></span><span>，一个</span><span><font face="Times New Roman">join</font></span><span>。在所有的情形中，工作流的</span><span><font face="Times New Roman">state</font></span><span>跟着变化（例子工作流中的</span><span><font face="Times New Roman">states</font></span><span>分别为：</span><span><font face="Times New Roman">Underway,Queued,</font></span><span>和</span><span><font face="Times New Roman">finished</font></span><span>）</span></font></p>
<p><span><font face="Times New Roman"><font size=3>6、</font>&nbsp;</font></span><font size=3><span>如果一个</span><span><font face="Times New Roman">result</font></span><span>引起一个</span><span><font face="Times New Roman">split</font></span><span>，这个</span><span><font face="Times New Roman">result</font></span><span>会指定</span><span><font face="Times New Roman">split</font></span><span>的属性，以指向一个</span><span><font face="Times New Roman">split</font></span><span>元素。</span></font></p>
<p><span><font face="Times New Roman"><font size=3>7、</font>&nbsp;</font></span><font size=3><span>一个</span><span><font face="Times New Roman">split</font></span><span>可以有一个或者多个</span><span><font face="Times New Roman">unconditional results</font></span><span>，但是没有</span><span><font face="Times New Roman">conditional results</font></span><span>。</span><span><font face="Times New Roman">Unconditional results</font></span><span>。</span><span><font face="Times New Roman">Unconditional results</font></span><span>需要指定</span><span><font face="Times New Roman">steps</font></span><span>。</span></font></p>
<p><span><font face="Times New Roman"><font size=3>8、</font>&nbsp;</font></span><font size=3><span>一个</span><span><font face="Times New Roman">propertyset</font></span><span>是一个持久层数据的</span><span><font face="Times New Roman">map</font></span><span>，在全局应用中都是可用的。</span></font></p>
<p><span><font face="Times New Roman"><font size=3>9、</font>&nbsp;</font></span><font size=3><span>还有一种叫做</span><span><font face="Times New Roman">transientVars</font></span><span>的</span><span><font face="Times New Roman">map</font></span><span>，它只存活于一个工作流调用过程中的一定的生命周期，它将会对所有</span><span><font face="Times New Roman">functions</font></span><span>和</span><span><font face="Times New Roman">conditions</font></span><span>，包括所有的</span><span><font face="Times New Roman">registers</font></span><span>，</span><span><font face="Times New Roman">user input</font></span><span>，以及工作流上下文状态等起作用。</span></font></p>
<p><strong><span><font face="Times New Roman"><font size=3></font></font></span></strong>
<p><strong><font size=3><span>工作流概念：</span><span> </span></font></strong>
<p><font size=3><span>下面开始理解</span><span><font face="Times New Roman">osworkflow</font></span><span>的核心概念：</span></font></p>
<p><font size=3><span>对于</span><span><font face="Times New Roman">step</font></span><span>，</span><span><font face="Times New Roman">status</font></span><span>，</span><span><font face="Times New Roman">actions</font></span><span>部分就不多说明了，其实我觉得理解概念的最快方法应该是参照实例，即使我们不能用高高大大的词汇描绘出来，能自己理解是什么意思就可以了。</span></font></p>
<p><font size=3><span><font face="Times New Roman">Unconditional result </font></span><span>和</span><span><font face="Times New Roman"> conditional results</font></span></font></p>
<p><font size=3><span>这里做以简单介绍，对于每个</span><span><font face="Times New Roman">action</font></span><span>，要求至少存在一个</span><span><font face="Times New Roman">Unconditional&nbsp;result</font></span><span>，一个</span><span><font face="Times New Roman">result</font></span><span>也就是通过一系列指示来告诉</span><span><font face="Times New Roman">osworkflow</font></span><span>下一步的任务是干什么。这种调用使得产生变迁进而从一个</span><span><font face="Times New Roman">state</font></span><span>到另外一个</span><span><font face="Times New Roman">state</font></span><span>。这种概念是在</span><span><font face="Times New Roman">UML</font></span><span>的状态机里有讲，希望了解状态机相关概念的可以到</span><span><font face="Times New Roman">UML</font></span><span>相关书籍中查看。</span></font></p>
<p><font size=3><span><font face="Times New Roman">Conditional result</font></span><span>是</span><span><font face="Times New Roman">unconditional result</font></span><span>的一种扩展。不同的地方在于他需要一些子元素：</span><span><font face="Times New Roman">condition</font></span><span>。用</span><span><font face="Times New Roman">and </font></span><span>和</span><span><font face="Times New Roman"> or</font></span><span>来标志各个</span><span><font face="Times New Roman">condition</font></span><span>之间的关系。</span></font></p>
<p><font size=3><span><font face="Times New Roman">Conditional </font></span><span>和</span><span><font face="Times New Roman">unconditional </font></span><span>的最终</span><span><font face="Times New Roman">result</font></span><span>可以产生三种效应或者说是结果：</span></font></p>
<p><span><font face="Times New Roman"><font size=3>1、</font>&nbsp;</font></span><font size=3><span>一个新的</span><span><font face="Times New Roman">step/status</font></span></font></p>
<p><span><font face="Times New Roman"><font size=3>2、</font>&nbsp;</font></span><font size=3><span>一个</span><span><font face="Times New Roman">split</font></span><span>，出现一或多个</span><span><font face="Times New Roman">step/status</font></span></font></p>
<p><span><font face="Times New Roman"><font size=3>3、</font>&nbsp;</font></span><font size=3><span>一个</span><span><font face="Times New Roman">join</font></span><span>，一个新的</span><span><font face="Times New Roman">step/status</font></span></font></p>
<p><font size=3><span>普遍的，一个</span><span><font face="Times New Roman">split</font></span><span>或者</span><span><font face="Times New Roman">join</font></span><span>不能</span><span><font face="Times New Roman">result</font></span><span>出另外一个</span><span><font face="Times New Roman">split</font></span><span>或</span><span><font face="Times New Roman">join</font></span><span>。</span></font></p>
<p><font size=3><span>一个</span><span><font face="Times New Roman">step/status result</font></span><span>可以按下面方式简单的设定：</span></font></p>
<p align=left><span>&lt;unconditional-result old-status="Finished" step="2" </span>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>status="Underway" owner="${someOwner}"/&gt;&nbsp;</span>&nbsp;
<p><font size=3><span>从一个</span><span><font face="Times New Roman">state split </font></span><span>到多个</span><span><font face="Times New Roman"> states</font></span><span>可以按以下方式达到：</span></font></p>
<pre><span><font face=宋体 size=3>&lt;unconditional-result split="1"/&gt;</font></span></pre>
<pre><span><font face=宋体 size=3>...</font></span></pre>
<pre><span><font face=宋体 size=3>&lt;splits&gt;</font></span></pre>
<pre><span><font face=宋体><font size=3>&nbsp;&lt;split id="1"&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp; </span>&lt;unconditional-result old-status="Finished" step="2" </font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>status="Underway" owner="${someOwner}"/&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp; </span>&lt;unconditional-result old-status="Finished" step="2" </font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>status="Underway" owner="${someOtherOwner}"/&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3>&nbsp;&lt;/split&gt;</font></font></span></pre>
<pre><span><font face=宋体 size=3>&lt;/splits&gt;</font></span></pre>
<p><font size=3><span><font face="Times New Roman">Joins</font></span><span>是比较复杂的用例。一个典型的</span><span><font face="Times New Roman">join</font></span><span>看起来大致如下：</span></font></p>
<pre><span><font face=宋体 size=3>&lt;!-- <span><font color=#000066>for</font></span> step id 6 -&gt;</font></span></pre>
<pre><span><font face=宋体 size=3>&lt;unconditional-result join="1"/&gt;</font></span></pre>
<pre><span><font face=宋体 size=3>...</font></span></pre>
<pre><span><font face=宋体 size=3>&lt;!- <span><font color=#000066>for</font></span> step id 8 -&gt;</font></span></pre>
<pre><span><font face=宋体 size=3>&lt;unconditional-result join="1"/&gt;</font></span></pre>
<pre><span><font face=宋体 size=3>...</font></span></pre>
<pre><span><font face=宋体 size=3>&lt;joins&gt;</font></span></pre>
<pre><span><font face=宋体><font size=3>&nbsp;&lt;join id="1"&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp; </span>&lt;join id="1"&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp; </span>&lt;conditions type="AND"&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;condition type="beanshell"&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;&lt;arg name="script"&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>"Finished".equals(jn.getStep(6).getStatus() </font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&amp;&amp; "Finished".equals(jn.getStep(8).getStatus())</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;/arg&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;/condition&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp; </span>&lt;/conditions&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3>&nbsp;&lt;/join&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3>&nbsp;&lt;unconditional-result old-status="Finished" status="Underway" </font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>owner="test" step="2"/&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3>&nbsp;&lt;/join&gt;</font></font></span></pre>
<pre><span><font face=宋体 size=3>&lt;/joins&gt;</font></span></pre>
<p><font size=3><span>上面这段代码中最需要关心的就应该是</span><span><font face="Times New Roman">jn</font></span><span>。当</span><span><font face="Times New Roman">join</font></span><span>实际发生的时候，这个特殊的变量</span><span><font face="Times New Roman">jn</font></span><span>可以被用来建立表达式。本质上，可以很容易理解出这个段</span><span><font face="Times New Roman">xml</font></span><span>的意思就是：当</span><span><font face="Times New Roman">step6</font></span><span>和</span><span><font face="Times New Roman">8</font></span><span>都</span><span><font face="Times New Roman">finish</font></span><span>时候在此处进行</span><span><font face="Times New Roman">join</font></span><span>。</span></font></p>
<p><font size=3><span><font face="Times New Roman">Functions</font></span><span>部分：</span></font></p>
<p><font size=3><span><font face="Times New Roman">Osworkflow</font></span><span>用</span><span><font face="Times New Roman">function</font></span><span>来定义商业逻辑和一些需要定义执行的服务。用</span><span><font face="Times New Roman">functions</font></span><span>标签来表示。</span></font></p>
<p><font size=3><span>两种</span><span><font face="Times New Roman">functions</font></span><span>（</span><span><font face="Times New Roman">pre</font></span><span>和</span><span><font face="Times New Roman">post</font></span><span>）</span></font></p>
<p><font size=3><span><font face="Times New Roman">Pre </font></span><span>是在工作流进行某个变迁之前需要被执行的。一个比较好的例子：为了在</span><span><font face="Times New Roman">result</font></span><span>中</span><span><font face="Times New Roman">state</font></span><span>变更产生，而先建立</span><span><font face="Times New Roman">caller</font></span><span>。</span></font></p>
<p><font size=3><span><font face="Times New Roman">Post</font></span><span>是在之后执行的。如当某一个</span><span><font face="Times New Roman">state</font></span><span>改变后，发送一</span><span><font face="Times New Roman">email</font></span><span>到某处。</span></font></p>
<p><font size=3><span><font face="Times New Roman">Functions</font></span><span>可以在两个分别的地方被指定：</span><span><font face="Times New Roman">steps</font></span><span>和</span><span><font face="Times New Roman">actions</font></span><span>。</span></font></p>
<p><span><font face="Times New Roman" size=3>Trigger Functions</font></span></p>
<p><span><font face="Times New Roman" size=3>Validators</font></span></p>
<p><span><font face="Times New Roman" size=3>Registers</font></span></p>
<p><font size=3><span>一个</span><span><font face="Times New Roman">function</font></span><span>：用来返回一个用以被其他普通对象能够容易访问得到的对象。尤其是指</span><span><font face="Times New Roman">workflow </font></span><span>的实体类。返回的对象类型不闲典型的例子如：</span><span><font face="Times New Roman">document</font></span><span>，</span><span><font face="Times New Roman">metadata</font></span><span>，</span><span><font face="Times New Roman">issue</font></span><span>，</span><span><font face="Times New Roman">task</font></span><span>等。非常便利。</span></font></p>
<pre><span><font face=宋体 size=3>&lt;registers&gt;</font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;register name="doc" class="com.acme.DocumentRegister"/&gt;</font></font></span></pre>
<pre><span><font face=宋体 size=3>&lt;/registers&gt;</font></span></pre>
<pre><span><font face=宋体 size=3>...</font></span></pre>
<pre><span><font face=宋体 size=3>&lt;results&gt;</font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;result condition="doc.priority == 1" step="1" status="Underway" </font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>owner="${someManager}"/&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;unconditional-result step="1" status="Queued"/&gt;</font></font></span></pre>
<pre><span><font face=宋体 size=3>&lt;/results&gt;</font></span></pre>
<p><span><font face="Times New Roman" size=3>Conditions</font></span></p>
<p><span><font face="Times New Roman"><font size=3></font></font></span>
<p><span><font size=3>变量</font></span></p>
<p><span><font face="Times New Roman"><font size=3></font></font></span>
<p><span><font size=3>授权与限权</font></span></p>
<p><font size=3><span>自动执行的</span><span><font face="Times New Roman">action </font></span></font></p>
<p><font size=3><span>设置</span><span><font face="Times New Roman">auto=true</font></span></font></p>
<p><font size=3><span><font face="Times New Roman">Common and global actions</font></span><span>：</span></font></p>
<p><font size=3><span><font face="Times New Roman">Common</font></span><span>和</span><span><font face="Times New Roman">globalactions</font></span><span>的主要作用在于在工作流定义文件中能够避免代码重复。</span></font></p>
<p><font size=3><span>基本思想就是简单。这两种</span><span><font face="Times New Roman">actions</font></span><span>是在最开始就进行说明的，在</span><span><font face="Times New Roman">initial-actions</font></span><span>元素后面。</span></font></p>
<p><span><font size=3>这两处还不能完全理解好！</font></span></p>
<p><font size=3><span><font face="Times New Roman">Common actions</font></span><span>：在最开始定义好，可以在其他地方如此引用</span></font></p>
<p><span><font face="Times New Roman" size=3>&lt;common-action id="100" /&gt;</font></span></p>
<p><font size=3><span>例如一个&#8220;</span><span><font face="Times New Roman">send mail</font></span><span>&#8221;的</span><span><font face="Times New Roman">action</font></span></font></p>
<p><font size=3><span><font face="Times New Roman">Global actions</font></span><span>：不同之处在于显式的被某一个</span><span><font face="Times New Roman">step</font></span><span>引用。它通常对所有的</span><span><font face="Times New Roman">steps</font></span><span>都是可用的。一个例子：&#8220;终止工作流&#8221;，在任何一步，都有可能终止工作流。</span></font></p>
<p><span><font face="Times New Roman"><font size=3></font></font></span>
<p><font size=3><span>需要注意的是：这两种</span><span><font face="Times New Roman">actions</font></span><span>要具备唯一的</span><span><font face="Times New Roman">ID</font></span><span>，而不能和其他</span><span><font face="Times New Roman">action</font></span><span>的</span><span><font face="Times New Roman">ID</font></span><span>重复。</span></font></p>
<p><span><font face="Times New Roman"><font size=3></font></font></span>
<p><font size=3><span>接下来主要讲解关于</span><span><font face="Times New Roman">function</font></span><span>的四种情况：</span></font></p>
<p><font size=3><span><font face="Times New Roman">osworkflow</font></span><span>中的</span><span><font face="Times New Roman">function</font></span><span>就是可以在变迁之前或者后进行执行的内容。</span></font></p>
<p><span><font face="Times New Roman"><font size=3>1、</font>&nbsp;</font></span><font size=3><span>基于</span><span><font face="Times New Roman">java</font></span><span>的</span><span><font face="Times New Roman">functions</font></span></font></p>
<p><font size=3><span>从</span><span><font face="Times New Roman">classloader</font></span><span>中加载</span><span><font face="Times New Roman">java </font></span><span>类，通过</span><span><font face="Times New Roman">jndi</font></span><span>找会</span><span><font face="Times New Roman">java</font></span><span>类，远程</span><span><font face="Times New Roman">ejbs</font></span><span>，本地</span><span><font face="Times New Roman">ejb</font></span><span>。</span></font></p>
<p><font size=3><span>这一类型的</span><span><font face="Times New Roman">function</font></span><span>必须实现接口：</span><strong><span><font face="Times New Roman">com.opensymphony.workflow.FunctionProvider</font></span></strong><strong><span>，</span></strong><span>在这个接口中有一个方法</span><span><font face="Times New Roman">execute</font></span><span>。这个方法（</span><span><font face="Times New Roman">execute</font></span><span>）需要三个参数</span><strong><span> </span></strong></font>
<p><font size=3><span>可以自己去查</span><span><font face="Times New Roman">api</font></span><span>即可找到这三个参数，两个</span><span><font face="Times New Roman">map</font></span><span>一个</span><span><font face="Times New Roman">propertyset</font></span></font></p>
<p><font size=3><span>基于</span><span><font face="Times New Roman">java</font></span><span>的</span><span><font face="Times New Roman">functions</font></span><span>在以下类型是可用的。</span></font></p>
<p><font size=3><span>（</span><span><font face="Times New Roman">1</font></span><span>）、</span><span><font face="Times New Roman">class</font></span></font></p>
<p><font size=3><span>对于一个</span><span><font face="Times New Roman">class function</font></span><span>，</span><span><font face="Times New Roman">classloader</font></span><span>必须能够知道你的</span><span><font face="Times New Roman">function</font></span><span>的类名。如下：</span></font></p>
<div>
<pre><span><font face=宋体 size=3>&lt;function type="class"&gt;</font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="class.name"&gt;com.acme.FooFunction&lt;/arg&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="message"&gt;The message is ${message}&lt;/arg&gt;</font></font></span></pre>
</div>
<p><span><font face="Times New Roman" size=3>&lt;/function&gt;</font></span></p>
<p><font size=3><span>（</span><span><font face="Times New Roman">2</font></span><span>）、</span><span><font face="Times New Roman">jndi</font></span></font></p>
<p><font size=3><span><font face="Times New Roman">jndi function</font></span><span>与</span><span><font face="Times New Roman"> class</font></span><span>想类似，当然必须已经确实存在于</span><span><font face="Times New Roman">jndi</font></span><span>树中，在这里，需要一个</span><span><font face="Times New Roman">jndi.location</font></span><span>来进行配置：</span></font></p>
<div>
<pre><span><font face=宋体 size=3>&lt;function type="jndi"&gt;</font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="jndi.location"&gt;java:/FooFunction&lt;/arg&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="message"&gt;The message is ${message}&lt;/arg&gt;</font></font></span></pre>
</div>
<p><span><font face="Times New Roman" size=3>&lt;/function&gt;</font></span></p>
<p><font size=3><span>（</span><span><font face="Times New Roman">3</font></span><span>）、</span><span><font face="Times New Roman">remote-ejb</font></span></font></p>
<p><span><font size=3>这部分跳过</font></span></p>
<div>
<pre><span><font face=宋体 size=3>&lt;function type="remote-ejb"&gt;</font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="ejb.location"&gt;java:/comp/env/FooEJB&lt;/arg&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="message"&gt;The message is ${message}&lt;/arg&gt;</font></font></span></pre>
</div>
<p><span><font face="Times New Roman" size=3>&lt;/function&gt;</font></span></p>
<p><font size=3><span><font face="Times New Roman">(4)</font></span><span>、</span><span><font face="Times New Roman">local-ejb</font></span></font></p>
<p><span><font size=3>跳过</font></span></p>
<div>
<pre><span><font face=宋体 size=3>&lt;function type="local-ejb"&gt;</font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="ejb.location"&gt;java:/comp/env/FooEJB&lt;/arg&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="message"&gt;The message is ${message}&lt;/arg&gt;</font></font></span></pre>
</div>
<p><span><font face="Times New Roman" size=3>&lt;/function&gt;</font></span></p>
<p><font face="Times New Roman"><span><font size=3>2、</font>&nbsp;</span><span><font size=3>beanshell function</font></span></font></p>
<p><font size=3><span><font face="Times New Roman">osworkflow</font></span><span>支持</span><span><font face="Times New Roman">beanshell</font></span><span>，这是一种脚本语言。可以在</span><span><font face="Times New Roman">http://www.beanshell.org/</font></span><span>来查看</span><span><font face="Times New Roman">beanshell</font></span><span>的相关内容。</span></font></p>
<p><font size=3><span>在这种情况下，需要将</span><span><font face="Times New Roman">xml</font></span><span>过程定义中的</span><span><font face="Times New Roman">type</font></span><span>设置成</span><span><font face="Times New Roman">beanshell</font></span><span>。另外还需要有个一个参数名字为</span><span><font face="Times New Roman">script</font></span><span>的参数，此参数值是实际需要被执行的。</span></font></p>
<p><span><font size=3>例如：</font></span></p>
<div>
<pre><span><font face=宋体 size=3>&lt;function type="beanshell"&gt;</font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="script"&gt;&nbsp;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span><font color=#660066>System</font></span>.out.println("Hello, World!");</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;/arg&gt;</font></font></span></pre>
</div>
<p><span><font face="Times New Roman" size=3>&lt;/function&gt;</font></span></p>
<p><font size=3><span>三种变量：</span><span><font face="Times New Roman">entry</font></span><span>，</span><span><font face="Times New Roman">context</font></span><span>，</span><span><font face="Times New Roman">store</font></span></font></p>
<p><font size=3><span><font face="Times New Roman">entry</font></span><span>：实现</span><em><span><font face="Times New Roman">com.opensymphony.workflow.spi.WorkflowEntry</font></span></em><em><span>并且表示工作流实例。</span><span> </span></em></font>
<p><font size=3><em><span><font face="Times New Roman">Context</font></span></em><em><span>：</span><span><font face="Times New Roman">com.opensymphony.workflow.WorkflowContext</font></span></em><em><span>。允许</span><span><font face="Times New Roman">beanshell functions</font></span></em><em><span>回滚事务或者确定</span><span><font face="Times New Roman">caller</font></span></em><em><span>的</span><span><font face="Times New Roman">name </font></span></em></font>
<p><font size=3><em><span><font face="Times New Roman">Store</font></span></em><em><span>：</span><span><font face="Times New Roman">com.opensymphony.workflow.WorkflowStore</font></span></em><em><span>。</span></em><em><span>允许</span></em><em><span><font face="Times New Roman">function</font></span></em><em><span>访问工作流的持久存储层。</span></em><em><span>
<p></span></em></font>&nbsp;</p>
<div>
<pre><span><font face=宋体 size=3>&lt;function type="beanshell"&gt;</font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="script"&gt;&nbsp;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>propertySet.setString("world", "Earth");</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;/arg&gt;</font></font></span></pre>
<pre><span><font face=宋体 size=3>&lt;/function&gt;</font></span></pre>
<pre><span><font face=宋体 size=3>&lt;function type="beanshell"&gt;</font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="script"&gt;&nbsp;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span><font color=#660066>System</font></span>.out.println("Hello, "+propertySet.getString("world"));</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;/arg&gt;</font></font></span></pre>
</div>
<p><span><font face="Times New Roman" size=3>&lt;/function&gt;</font></span></p>
<p><span><font face="Times New Roman"><font size=3></font></font></span>
<p><font size=3><span>输出结果是&#8220;</span><span><font face="Times New Roman">hello</font></span><span>，</span><span><font face="Times New Roman">earth</font></span><span>&#8221;因为任何存储在</span><span><font face="Times New Roman">propertyset</font></span><span>中的变量将可以在整个工作流中被持久使用。</span></font></p>
<p><font face="Times New Roman"><span><font size=3>3、</font>&nbsp;</span><span><font size=3>BSF function(perlscript, vbscript, javascript)</font></span></font></p>
<p><font size=3><span>除了上面说过的两种</span><span><font face="Times New Roman">type</font></span><span>的</span><span><font face="Times New Roman">function</font></span><span>。还有</span><span><font face="Times New Roman">bsf</font></span><span>类型的</span><span><font face="Times New Roman">function</font></span><span>。</span></font></p>
<p><font size=3><span><font face="Times New Roman">BSF</font></span><span>（</span><span><font face="Times New Roman">bean scripting framework</font></span><span>）是</span><span><font face="Times New Roman">IBM</font></span><span>的一个组织做的一个通用环境可以使用</span><span><font face="Times New Roman">VBScript, Perlscript, Python, and JavaScript</font></span></font></p>
<div>
<pre><span><font face=宋体 size=3>&lt;function type="bsf"&gt;</font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="source"&gt;foo.pl&lt;/arg&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="row"&gt;0&lt;/arg&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="col"&gt;0&lt;/arg&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;arg name="script"&gt;</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>print $bsf-&gt;lookupBean("propertySet").getString("foo");</font></font></span></pre>
<pre><span><font face=宋体><font size=3><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&lt;/arg&gt;</font></font></span></pre>
</div>
<p><span><font face="Times New Roman" size=3>&lt;/function&gt;</font></span></p>
<p><span><font size=3>个人觉得这部分可以先跳过去，知道有这么一回事就可以了。</font></span></p>
<p><font face="Times New Roman"><span><font size=3>4、</font>&nbsp;</span><span><font size=3>Utility Function</font></span></font></p>
<p><font size=3><span>可以到</span><span><font face="Times New Roman">api</font></span><span>的</span><em><span><font face="Times New Roman">com.opensymphony.workflow.util</font></span></em><em><span>这部分查看。下面只是列出一些主要的功能。相当于</span></em><em><span><font face="Times New Roman">java.util</font></span></em><em><span>里的东西。</span></em></font></p>
<p><font size=3><span>主要用于创建动态的工作流定义，主要是包括一些实用功能，如</span><span><font face="Times New Roman">caller</font></span><span>、</span><span><font face="Times New Roman">webworkexecutor</font></span><span>、</span><span><font face="Times New Roman">ejbinvoker</font></span><span>、</span><span><font face="Times New Roman">jmsmessage</font></span><span>、</span><span><font face="Times New Roman">mostrecentowner</font></span><span>、</span><span><font face="Times New Roman">schedulejob</font></span><span>、</span><span><font face="Times New Roman">unschdulejob</font></span><span>、</span><span><font face="Times New Roman">sendmail</font></span><span>。</span></font></p>
<p><font size=3><span>这部分碰到不懂就去查</span><span><font face="Times New Roman">api</font></span><span>是个好办法，这里就不去多写的。</span></font></p>
<p><span><font face="Times New Roman" size=3>validators:</font></span></p>
<p><font size=3><span>与</span><span><font face="Times New Roman">functions</font></span><span>类似，</span><span><font face="Times New Roman">osworkflow</font></span><span>有下面几种不同形式的</span><span><font face="Times New Roman">validators</font></span><span>：</span><span><font face="Times New Roman">java-based</font></span><span>，</span><span><font face="Times New Roman">beanshell</font></span><span>，和</span><span><font face="Times New Roman">bsf</font></span><span>。</span><span><font face="Times New Roman">Java-based</font></span><span>的</span><span><font face="Times New Roman">validators</font></span><span>必须实现</span><strong><span><font face="Times New Roman">com.opensymphony.workflow.Validator</font></span></strong><span>接口（如果是</span><span><font face="Times New Roman">remote-ejb</font></span><span>，则需实现</span><strong><span><font face="Times New Roman">com.opensymphony.workflow.ValidatorRemote</font></span></strong><span>接口）。</span><span><font face="Times New Roman">Java-based</font></span><span>这种情况是通过抛出个</span><span><font face="Times New Roman">InvalidInputException</font></span><span>异常表明一个输入是不合法的，并且停止工作流</span><span><font face="Times New Roman">action</font></span><span>。</span></font></p>
<p><span><font face="Times New Roman"><font size=3></font></font></span>
<p><font size=3><span>在</span><span><font face="Times New Roman">beanshell</font></span><span>和</span><span><font face="Times New Roman">bsf</font></span><span>中，有一点小小不同，即使异常可以在脚本中抛出，但是不能抵达到</span><span><font face="Times New Roman">jre</font></span><span>。所以在</span><span><font face="Times New Roman">beanshell</font></span><span>和</span><span><font face="Times New Roman">bsf</font></span><span>中用错误信息来完成。逻辑如下：</span></font></p>
<p><span><font size=3>u</font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><font size=3><span>如果返回值是一个</span><span><font face="Times New Roman">InvalidInputException</font></span><span>对象，这个对象立刻抛出到</span><span><font face="Times New Roman">client</font></span><span>。</span></font></p>
<p><span><font size=3>u</font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><font size=3><span>如果返回值是一个</span><span><font face="Times New Roman">map</font></span><span>，</span><span><font face="Times New Roman">map</font></span><span>被用做一个</span><span><font face="Times New Roman">error/errormessage</font></span><span>对。</span></font></p>
<p><span><font size=3>u</font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><font size=3><span>如果返回值是一个</span><span><font face="Times New Roman">String []</font></span><span>，偶数字被做为</span><span><font face="Times New Roman">key</font></span><span>。奇数做为</span><span><font face="Times New Roman">value</font></span><span>来构造一个</span><span><font face="Times New Roman">map</font></span></font></p>
<p><span><font size=3>u</font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><font size=3><span>其他情况，把值转换成</span><span><font face="Times New Roman">string</font></span><span>并且作为一个普通的错误信息来添加。</span></font></p>
<p><span><font face="Times New Roman" size=3>Registers:</font></span></p>
<p><font size=3><span><font face="Times New Roman">Register</font></span><span>是一个在工作流定义文件中用来动态注册的运行时（块）</span></font></p>
<p><font size=3><span>它也是和</span><span><font face="Times New Roman">validators</font></span><span>，</span><span><font face="Times New Roman">functions</font></span><span>差不多，都是能够以</span><span><font face="Times New Roman">java-based</font></span><span>，</span><span><font face="Times New Roman">beanshell</font></span><span>和</span><span><font face="Times New Roman">bsf</font></span><span>不同格式出现。</span></font></p>
<p><span><font face="Times New Roman" size=3>Conditions:</font></span></p>
<p><font size=3><span><font face="Times New Roman">Conditions</font></span><span>与</span><span><font face="Times New Roman">osworkflow</font></span><span>小小不相似之处在于：在</span><span><font face="Times New Roman">bsf</font></span><span>或者</span><span><font face="Times New Roman">beanshell</font></span><span>脚本中，有一个额外的对象叫做&#8220;</span><span><font face="Times New Roman">jn</font></span><span>&#8221;。这个</span><span><font face="Times New Roman">jn</font></span><span>来源于</span><font face="Times New Roman"><em><span>com.opensymphony.workflow.</span></em><em><span>joinnodes</span></em></font><em><span>。它的作用就是连接条件（</span></em><em><span><font face="Times New Roman">join-conditions</font></span></em><em><span>）。除此之外，</span></em><em><span><font face="Times New Roman">condition</font></span></em><em><span>必须有一个返回值（</span></em><em><span><font face="Times New Roman">true or false</font></span></em><em><span>）。</span></em><em><span> </span></em></font>
<p><font size=3><em><span><font face="Times New Roman">Condition</font></span></em><em><span>必须被</span></em><em><span><font face="Times New Roman">conditions</font></span></em><em><span>包含成为其子元素。</span></em><em><span><font face="Times New Roman">Conditions</font></span></em><em><span>有一个属性</span></em><em><span><font face="Times New Roman">type</font></span></em><em><span>。可以为</span></em><em><span><font face="Times New Roman">and</font></span></em><em><span>或者</span></em><em><span><font face="Times New Roman">or</font></span></em><em><span>。</span></em><em><span><font face="Times New Roman">And</font></span></em><em><span>表示：所有</span></em><em><span><font face="Times New Roman">condition</font></span></em><em><span>元素必须都为</span></em><em><span><font face="Times New Roman">true</font></span></em><em><span>或者</span></em><em><span><font face="Times New Roman">false</font></span></em><em><span>。</span></em><em><span><font face="Times New Roman">Or</font></span></em><em><span>表示只要有一个</span></em><em><span><font face="Times New Roman">condition</font></span></em><em><span>元素为</span></em><em><span><font face="Times New Roman">true</font></span></em><em><span>。如果你需要更多复杂的</span></em><em><span><font face="Times New Roman">condition</font></span></em><em><span>逻辑。可以考虑实现</span></em><em><span><font face="Times New Roman">condition</font></span></em><em><span>或者</span></em><em><span><font face="Times New Roman">conditionremote</font></span></em><em><span>接口，</span></em><em><span><font face="Times New Roman">beanshell</font></span></em><em><span>，</span></em><em><span><font face="Times New Roman">bsf</font></span></em><em><span>。如果只有一个</span></em><em><span><font face="Times New Roman">condition</font></span></em><em><span>子元素的时候，</span></em><em><span><font face="Times New Roman">conditions</font></span></em><em><span>的</span></em><em><span><font face="Times New Roman">type</font></span></em><em><span>属性值可以省略。</span></em><em><span> </span></em></font>
<p><font size=3><em><span>在</span></em><em><span><font face="Times New Roman">2.7</font></span></em><em><span>中。可以嵌套</span></em><em><span><font face="Times New Roman">conditions</font></span></em><em><span>，这样可以让你实现更为复杂的商业逻辑。</span></em><em><span>
<p>&nbsp;</span></em></font></p>
<p><font size=3><span>下面是一个写标准的</span><span><font face="Times New Roman">conditions</font></span></font></p>
<p><font size=3><strong><span><font face="Times New Roman">OSUserGroupCondition</font></span></strong><strong><span>、</span><span><font face="Times New Roman">StatusCondition</font></span></strong><strong><span>、</span><span><font face="Times New Roman">AllowOwnerOnlyCondition</font></span></strong><strong><span>、</span><span><font face="Times New Roman">DenyOwnerCondition </font></span></strong></font>
<p><font size=3><span><font face="Times New Roman">Soap</font></span><span>支持，暂时略！因为暂时可能涉及不到！</span></font></p>
<p><font size=3><span><font face="Times New Roman">Gui Designer</font></span><span>部分略。</span></font></p>
<img src ="http://www.blogjava.net/livery/aggbug/127788.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2007-07-03 11:32 <a href="http://www.blogjava.net/livery/articles/127788.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OSWorkflow实现业务流程</title><link>http://www.blogjava.net/livery/articles/127762.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Tue, 03 Jul 2007 02:37:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/127762.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/127762.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/127762.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/127762.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/127762.html</trackback:ping><description><![CDATA[Osworkflow是完全用java语言编写的开放源代码的工作流引擎，具有显著的灵活性及完全面向有技术背景的用户的特点。用户可以根据自身的需求利用这款开源软件设计简单或是复杂的工作流。通过使用，用户就可以把工作中心放在业务和规则的定义上，而不需通过硬编码的方式实现一个Petri网或是一个有穷自动机。用户可以以最小的代价把osworkflow整合到自己的程序中来。Osworkflow几乎提供了所有用户可能在实际流程定义中需要用到的工作流构成元素，如：环节（step）、条件（conditions）、循环（loops）、分支（spilts）、合并（joins）、角色（roles）等等。(假如读者对这些概念还不熟悉，笔者将在Osworkflow基本概念一节中进行简单描述。)<br>&nbsp;&nbsp;&nbsp;&nbsp;但是，这款开源软件的文档十分匮乏，而且在大多数现实情形中并不适用。本文将尝试为读者填平实际的用例需求与十分简单的说明文档间的鸿沟。<br>&nbsp;&nbsp;&nbsp;&nbsp;用户可以在OpenSymphony的网站上下载osworkflow的发布。当前的最高版本是2.7（译者注：最新版本为2.8）.&nbsp;解压缩发布的软件包，即得到二进制程序、源代码、API文档、说明文档等。用户可以在软件的论坛和维基上获得进一步的帮助。<br><br>什么是工作流？<br>&nbsp;&nbsp;&nbsp;&nbsp;维基百科（Wikipedia，WP）把工作流定义为&#8220;一份工作的操作过程&#8221;：任务如何组成、如何操作、相关顺序如何、如何同步、信息如何流动以支持这些任务、以及任务如何被跟踪等。<br>&nbsp;&nbsp;&nbsp;&nbsp;一个工作流引擎实现了业务的流程处理。用户应可以自动跟踪过程，这将使得引擎更具效率。同时用户可以对工作流进行建模，监控及统计引擎数据等。<br><br>示例业务过程：贷款程序<br>&nbsp;&nbsp;&nbsp;&nbsp;本文的示例业务过程研究一个贷款应用程序的实例。我们将通过一个利用osworkflow工作流引擎的工作流来实现它。这个过程会在每家银行及金融机构中出现,&nbsp;其区别仅仅体现为有更过的部门或更多地文档需要处理.&nbsp;在本示例中,&nbsp;我们会用尽量简单的方法来实现这个业务流程以便于用户理解。图一描述了这个业务流程<br>&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.360doc.com/DownloadImg/73/130227_1.JPG">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>图一&nbsp;贷款业务流程<br><br>&nbsp;&nbsp;&nbsp;&nbsp;过程非常简单，分为如下4步，如下所示：<br>1）&nbsp;&nbsp;&nbsp;&nbsp;填写表格：银行客户填写表格申请贷款。<br>2）&nbsp;&nbsp;&nbsp;&nbsp;风险分析：一位风险分析家评估不良贷款的风险。<br>3）&nbsp;&nbsp;&nbsp;&nbsp;财务历史审查：财务管理官员负责检查客户历史贷款、应付账单、信用卡历史纪录等信息。<br>4）&nbsp;&nbsp;&nbsp;&nbsp;最终决定（同意/拒绝）：银行部门主管根据风险分析情况及财务历史审查情况最终决定是否贷款给该客户。<br><br>正如我们之前看到的，每个工作流都包含角色，每个角色都包含被分配的任务。下文说明了业务流程中涉及到的角色：<br>1）&nbsp;&nbsp;&nbsp;&nbsp;前台职员：在某个银行部门向顾客提供信贷申请表的雇员。<br>2）&nbsp;&nbsp;&nbsp;&nbsp;财务官员：负责检查申请者历史财务情况（往期贷款、未支付帐单等等）的职员。<br>3）&nbsp;&nbsp;&nbsp;&nbsp;风险分析家：负责分析将钱给予借贷申请者的外部因素（比如社会经济情况等）及借贷者本人的个人情况。<br>4）&nbsp;&nbsp;&nbsp;&nbsp;银行部门经理：负责最终决定是否给予借贷者贷款的经理。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;请记住，&#8220;信贷申请表&#8221;是一个重要的概念（我们将在系统实现一节看到其重要性）因为它是流经整个工作流的业务数据。<br><br>基本工作流概念<br><br>&nbsp;&nbsp;&nbsp;&nbsp;笔者在开篇曾介绍osworkflow提供了一些特有的构造，现在笔者将逐一介绍它们。<br>&nbsp;&nbsp;&nbsp;&nbsp;首先，在osworkflow中读者需要了解得最重要的概念是环节，每个工作流包含了多个环节，读者可以把环节想象成工作流中每一个重要的活动。每个环节可以有一些诸如&#8220;已完成&#8221;、&#8220;正在处理&#8221;、&#8220;已添加至处理队列&#8221;、&#8220;未处理&#8221;等的状态，设计工作流的人可以根据需要自己定义状态。<br>在每个环节，动作被用户指定为自动或手动地执行。每个动作执行后，都有一个结果（result）。结果决定了工作流的流转方向：可以停留在同一环节，跳转到另一环节，跳转到一个分支，或者汇集到一个合并等。<br>最后两个概念涉及用户对业务流程的并发执行，分支把工作流分解为两个并行的环节，合并则在用户满足一定条件后，把两个并行的环节合并成一个。<br>动作的执行代表了业务流程的执行，每个动作都有一组预处理功能（pre-functions）和一组后处理功能（post-functions）。其作用正如读者想象的那样，一个在动作触发之前执行，一个在动作触发之后执行。一个简单的例子是：可以在预处理功能中检验申请表格数据的正确性，而后在后处理贡功能中把经检验的数据保存至数据库。<br>动作的执行结果可以是有条件的（conditional）或无条件的（unconditional）。对于有条件的结果，引擎将首先检查是否条件被满足，然后再交给工作流来处理。如果条件不满足的话，引擎将进一步判断下一个有条件结果是否得到满足，以此类推，直到系统最终执行到无条件结果进行处理。<br>读者可能会问，如果所有的条件结果都没有得到满足会如何呢？事实上，每个动作都强制要求具有唯一一个无条件结果。与此对应的，可以有多个有条件的结果。<br>业务规则常常在最终结果中带有条件判断，比如，&#8220;如果申请来自于一个老客户，则流转到环节1&#8221;或者&#8220;如果当前系统的用户的角色是经理的话，直接流转道最后一个环节&#8221;。<br>最后一个重要的概念是步骤状态（process&nbsp;state），在osworkflow中，当前步骤状态是所有当前环节状态的集合。读者可能会认为工作流在运行过程中只能有一个状态，但现实的情况是：因为对分支和合并的支持，引擎能够做到对环节的并发控制，因此工作流的当前状态就可能出现：&#8220;等待风险分析及已核查财务历史&#8221;的情况。<br>激活动作的用户被顺理成章地称为触发者（caller），每个环节都有一个所有者（owner），以代表在当前环节中负责执行动作的角色或用户。<br>当用户在环节中运转流程的时候，已完成的环节被保存至历史表中（history），用户当前所处的环节成为当前环节（current&nbsp;steps）。<br>最后，读者可能注意到，在osworkflow中并不存在其他工作流引擎中所包含的工作项（workitem）的概念。这是因为osworkflow是&#8220;十分底层&#8221;的工作流实现，怎样实现或定义工作项完全交由用户来决定。笔者认为工作项的概念太过抽象，用业务数据来称呼它或许更为贴切一些。<br>Osworkflow的文档中介绍了更多的构造元素，如寄存器（Registers），共用方法（common&nbsp;functions）等，但笔者建议在建立好第一个工作流以后再去研究它们。它们是osworkflow基本元素外的高级特性，而我们前面所认识的元素则是osworkflow的根本所在。<br><br>Osworkflow体系结构<br><br>&nbsp;&nbsp;&nbsp;&nbsp;我们将在本节分析控制osworkflow的体系结构，我们需要理解它是怎样适用到我们的程序中来的。如我们所猜想的，Osworkflow最主要的接口是Workflow，这个接口也是整个工作流引擎的入口点。是整个系统的门面（facade）。<br>&nbsp;&nbsp;&nbsp;&nbsp;接口的实现主要关注具体的业务操作能力，这个接口定义了工作流查询，获取当前的可执行动作，执行动作，显示历史环节等。<br>&nbsp;&nbsp;&nbsp;&nbsp;工作流被持久化在工作流存储体（Workflow&nbsp;Store）中，osworkflow提供了几种持久化的方法，包括Hibernate持久化集成，JDBC持久化集成等。一个存储体包含了环节信息，变量，工作流自身的描述信息等等。<br>&nbsp;&nbsp;&nbsp;&nbsp;用户可能遇到的最常见的应用模式如下所示：<br>1）&nbsp;&nbsp;&nbsp;&nbsp;通过给定的状态在工作流存储体中查询工作流信息，通常还根据某一个工作流程中具有需执行动作的用户来进行查询。这种查询时通过WorkflowQuery对象中的Workflow.query()方法实现的。<br>2）&nbsp;&nbsp;&nbsp;&nbsp;通过getAvailableActions()方法列出所有在满足条件查询结果中可执行的操作。<br>3）&nbsp;&nbsp;&nbsp;&nbsp;通过doAction()方法执行用户选择的动作。在执行动作的时候一些执行参数可以以java.util.Map的形式传递，以实现在工作流定义的运行期进行信息的传递。<br>4）&nbsp;&nbsp;&nbsp;&nbsp;用户可以有选择地通过调用initialize()实例化一个工作流。<br><br>在理想情况下，由业务逻辑层负责调用osworkflow中的方法，如图二所示：<br>&nbsp;<img src="http://www.360doc.com/DownloadImg/73/130227_2.JPG"><br>图二&nbsp;在业务逻辑中集成工作流<br><br>在osworkflow中，业务逻辑描述在一个XML文件中，称为工作流描述符（workflow&nbsp;descriptor.）。我们将在实现小节中建立一个简单的描述符。在工作流描述符中的功能（functions）和条件（conditions）中，用户可以定义自己的业务逻辑。笔者将在把工作流集成到应用程序中一节中进行论述。<br><br>实现<br><br>&nbsp;&nbsp;&nbsp;&nbsp;本小节介绍如何把一个业务逻辑抽象成一个工作流。首先我们要在业务流程图中识别出工作流的环节。如图一所示，显然，我们共有四个环节，同时包含一个分支及一个合并。在下面的bank.xml文件中，读者将看到它们在描述符中是如何被表示的。<br>&nbsp;&nbsp;&nbsp;&nbsp;建立好环节以后，必须在每个环节中添加一些动作以便于工作流运转。每个动作有唯一的无条件结果，条件结果由读者有选择地来实现。<br>&lt;step&nbsp;id="1"&nbsp;name="Form&nbsp;Filling"&gt;<br>&nbsp;&lt;actions&gt;<br>&nbsp;&nbsp;&lt;action&nbsp;id="2"&nbsp;name="Fill&nbsp;Form"&gt;<br>&nbsp;&nbsp;&nbsp;&lt;results&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;unconditional-result&nbsp;old-status="Finished"&nbsp;split="1"/&gt;<br>&nbsp;&nbsp;&nbsp;&lt;/results&gt;<br>&nbsp;&nbsp;&lt;/action&gt;<br>&nbsp;&lt;/actions&gt;<br>&lt;/step&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;接下来要并行地执行风险分析和财务历史核查，这里是放置分支的最理想地点。<br>&lt;splits&gt;<br>&nbsp;&lt;split&nbsp;id="1"&gt;<br>&nbsp;&nbsp;&lt;unconditional-result&nbsp;old-status="Finished"<br>&nbsp;&nbsp;&nbsp;&nbsp;status="Underway"&nbsp;owner="Risk&nbsp;Analyst"&nbsp;step="2"/&gt;<br>&nbsp;&nbsp;&lt;unconditional-result&nbsp;old-status="Finished"<br>&nbsp;&nbsp;&nbsp;&nbsp;status="Underway"&nbsp;owner="Financial&nbsp;Officer"&nbsp;step="3"/&gt;<br>&nbsp;&lt;/split&gt;<br>&lt;/splits&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;在部门经理最终确认以前，并发的工作流环节必须得到合并。我们可以通过应用一个合并（join）来实现它，合并通过一个条件告诉工作流引擎，是否可以合并并进行到下一环节。在本例中，我们假设这个条件为：前面两个环节都已具有&#8220;Finished&#8221;的结束状态。即当风险分析或财务审核任何一个未完成前，不能进行到下一步：<br>&lt;joins&gt;<br>&nbsp;&lt;join&nbsp;id="1"&gt;<br>&nbsp;&nbsp;&lt;conditions&nbsp;type="AND"&gt;<br>&nbsp;&nbsp;&nbsp;&lt;condition&nbsp;type="beanshell"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;arg&nbsp;name="script"&gt;&lt;![CDATA[<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Finished".equals(jn.getStep(2).getStatus())&nbsp;&amp;&amp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Finished".equals(jn.getStep(3).getStatus())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]]&gt;&lt;/arg&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/condition&gt;<br>&nbsp;&nbsp;&nbsp;&lt;/conditions&gt;<br>&nbsp;&nbsp;&lt;unconditional-result&nbsp;old-status="Finished"<br>&nbsp;&nbsp;&nbsp;&nbsp;status="Underway"&nbsp;owner="Manager"&nbsp;step="4"/&gt;<br>&nbsp;&lt;/join&gt;<br>&lt;/joins&gt;<br>下面在描述符中加入每个环节的所有者，正如我们在基本概念一节看到的那样，所有者通常代表了环节间交互的角色，角色的引入默认情况下通过osuser框架来实现。<br>&nbsp;&nbsp;&nbsp;&nbsp;用户既可以手写XML描述符文件，也可以通过osworkflow提供的设计器来实现。读者可以在OpenSymphony的网站上试用这个工具。<br><br>测试实现<br><br>&nbsp;&nbsp;&nbsp;&nbsp;测试时，我们可以应用osworkflow提供的例子，把bank.xml放到WEB-INF/classes文件夹下，在文件workflows.xml中添加一行以使引擎能够识别这个工作流。<br><br>把工作流集成到应用程序<br><br>&nbsp;&nbsp;&nbsp;&nbsp;在建模及测试工作流之后，我们即可以通过下面的几行代码把osworkflow集成到我们的程序中。<br>Workflow&nbsp;wf&nbsp;=&nbsp;new&nbsp;BasicWorkflow(username);&nbsp;<br>HashMap&nbsp;inputs&nbsp;=&nbsp;new&nbsp;HashMap();<br>inputs.put("docTitle",&nbsp;request.getParameter("title"));<br>wf.initialize("workflowName",&nbsp;1,&nbsp;inputs);<br>&nbsp;&nbsp;&nbsp;&nbsp;inputs哈希表包含了初始工作流动作中需要传出的参数,有几个实现了Workflow接口的类，其中BasicWorkflow是不支持事务的简单实现。在工作流执行过程中可以在流程中调用外部的方法，这种方法应该是实现了FunctionProvider接口的方法类。然后我们就可以用以下的方式调用它：<br><br>&lt;action&nbsp;id="1"&nbsp;name="Execute&nbsp;business&nbsp;rule"&gt;<br>&nbsp;&lt;pre-functions&gt;<br>&nbsp;&nbsp;&lt;function&nbsp;type="class"&gt;<br>&nbsp;&nbsp;&nbsp;&lt;arg&nbsp;name="class.name"&gt;java.net.DroolsExecutorFunction&lt;/arg&gt;<br>&nbsp;&nbsp;&nbsp;&lt;arg&nbsp;name="ruleBaseName"&gt;BusinessRules.drl&lt;/arg&gt;<br>&nbsp;&nbsp;&lt;/function&gt;<br>&lt;/pre-functions&gt;<br>...<br>用户可以通过实现Condition接口，添加自己的条件控制。FunctionProvider及Condition接口可以调用工作流中的已知方法，这两个接口都可以接受来自于XML描述符文件中的参数。<br><br>结论<br><br>&nbsp;&nbsp;&nbsp;&nbsp;把一个业务流程抽象成一个工作流的任务并不容易，需要好的方法和合适的工具，osworkflow是一个为我们提供了许多可重用结构的理想工具。希望通过对本文的阅读，读者能够理解最基本的osworkflow概念。本文论述过程中所采用的方法非常基础和简单，但却值得借鉴。<br>
<img src ="http://www.blogjava.net/livery/aggbug/127762.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2007-07-03 10:37 <a href="http://www.blogjava.net/livery/articles/127762.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OSWorkflow官方样例</title><link>http://www.blogjava.net/livery/articles/127761.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Tue, 03 Jul 2007 02:36:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/127761.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/127761.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/127761.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/127761.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/127761.html</trackback:ping><description><![CDATA[<h3>目的</h3>
<p>这篇指导资料的目的是介绍OSWorkflow的所有概念，指导你如何使用它，并且保证你逐步理解OSWorkflow的关键内容。</p>
<p>本指导资料假定你已经部署OSWorkflow的范例应用在你的container上。范例应用部署是使用基于内存的数据存储，这样你不需要担心如何配置其他持久化的例子。范例应用的目的是为了说明如何应用OSWorkflow，一旦你精通了OSWorkflow的流程定义描述符概念和要素，应该能通过阅读这些流程定义文件而了解实际的流程。</p>
<p>本指导资料目前有3部分：<br>1. <a href="http://www.duduwolf.com/post/osworkflow_tutorial.asp#1"><u><font color=#800080>你的第一个工作流</font></u></a><br>2. <a href="http://www.duduwolf.com/post/osworkflow_tutorial.asp#2"><u><font color=#800080>测试你的工作流</font></u></a><br>3. <a href="http://www.duduwolf.com/post/osworkflow_tutorial.asp#3"><u><font color=#800080>更多的流程定义描述符概念</font></u></a><br></p>
<h1><a name=1></a>1. Your first workflow </h1>
<h3>创建描述符</h3>
<p>首先，让我们来定义工作流。你可以使用任何名字来命名工作流。一个工作流对应一个XML格式的定义文件。让我们来开始新建一个&#8220;myworkflow.xml&#8221;的文件，这是样板文件：</p>
<div class=code>
<div class=codeContent>
<pre class=code-xml><span class=code-tag>&lt;?xml version=<span class=code-quote>"1.0"</span> encoding=<span class=code-quote>"UTF-8"</span>?&gt;</span>
&lt;!DOCTYPE workflow PUBLIC
<span class=code-quote>"-//OpenSymphony Group//DTD OSWorkflow 2.7//EN"</span>
<span class=code-quote>"http://www.opensymphony.com/osworkflow/workflow_2_7.dtd"</span>&gt;
<span class=code-tag>&lt;workflow&gt;</span>
<span class=code-tag>&lt;initial-actions&gt;</span>
...
<span class=code-tag>&lt;/initial-actions&gt;</span>
<span class=code-tag>&lt;steps&gt;</span>
...
<span class=code-tag>&lt;/steps&gt;</span>
<span class=code-tag>&lt;/workflow&gt;</span></pre>
</div>
</div>
<p>首先是标准的XML头部，要注意的是OSWorkflow将会通过这些指定的DTD来验证XML内容的合法性。你可以使用绝大多数的XML编辑工具来编辑它，并且可以highlight相应的错误。</p>
<h3>步骤和动作</h3>
<p>接下来我们来定义初始化动作和步骤。首先需要理解的OSWorkflow重要概念是steps （步骤） 和 actions （动作）。一个步骤是工作流所处的位置，比如一个简单的工作流过程，它可能从一个步骤流转到另外一个步骤（或者有时候还是停留在一样的步骤）。举例来说，一个文档管理系统的流程，它的步骤名称可能有&#8220;First Draft - 草案初稿&#8221;，&#8220;Edit Stage -编辑阶段&#8221;，&#8220;At publisher - 出版商&#8221;等。</p>
<p>动作指定了可能发生在步骤内的转变，通常会导致步骤的变更。在我们的文件管理系统中，在&#8220;草案初稿&#8221;这个步骤可能有&#8220;start first draft - 开始草案初稿&#8221;和&#8220;complete first draft - 完成草案初稿&#8221;这样2个动作。</p>
<p>简单的说，步骤是&#8220;在哪里&#8221;，动作是&#8220;可以去哪里&#8221;。</p>
<p>初始化步骤是一种特殊类型的步骤，它用来启动工作流。在一个工作流程开始前，它是没有状态，不处在任何一个步骤，用户必须采取某些动作才能开始这个流程。这些特殊步骤被定义在 &lt;initial-actions&gt;。</p>
<p>在我们的例子里面，假定只有一个简单的初始化步骤：&#8220;Start Workflow&#8221;，它的定义在里面&lt;initial-actions&gt;：</p>
<div class=code>
<div class=codeContent>
<pre class=code-xml><span class=code-tag>&lt;action id=<span class=code-quote>"1"</span> name=<span class=code-quote>"Start Workflow"</span>&gt;</span>
<span class=code-tag>&lt;results&gt;</span>
<span class=code-tag>&lt;unconditional-result old-status=<span class=code-quote>"Finished"</span> status=<span class=code-quote>"Queued"</span> step=<span class=code-quote>"1"</span>/&gt;</span>
<span class=code-tag>&lt;/results&gt;</span>
<span class=code-tag>&lt;/action&gt;</span></pre>
</div>
</div>
<p>这个动作是最简单的类型，只是简单地指明了下一个我们要去的步骤和状态。</p>
<h3>工作流状态</h3>
<p>工作流状态是一个用来描述工作流程中具体步骤状态的字符串。在我们的文档管理系统中，在&#8220;草案初稿&#8221;这个步骤可能有2个不同的状态：&#8220;Underway - 进行中&#8221;和&#8220;Queued - 等候处理中&#8221;</p>
<p>我们使用&#8220;Queued&#8221;指明这个条目已经被排入&#8220;First Draft&#8221;步骤的队列。比如说某人请求编写某篇文档，但是还没有指定作者，那么这个文档在&#8220;First Draft&#8221;步骤的状态就是&#8220;Queued&#8221;。&#8220;Underway&#8221;状态被用来指明一个作者已经挑选了一篇文档开始撰写，而且可能正在锁定这篇文档。</p>
<h3>第一个步骤</h3>
<p>让我们来看第一个步骤是怎样被定义在&lt;steps&gt;元素中的。我们有2个动作：第一个动作是保持当前步骤不变，只是改变了状态到&#8220;Underway&#8221;，第二个动作是移动到工作流的下一步骤。我们来添加如下的内容到&lt;steps&gt;元素：</p>
<div class=code>
<div class=codeContent>
<pre class=code-xml><span class=code-tag>&lt;step id=<span class=code-quote>"1"</span> name=<span class=code-quote>"First Draft"</span>&gt;</span>
<span class=code-tag>&lt;actions&gt;</span>
<span class=code-tag>&lt;action id=<span class=code-quote>"1"</span> name=<span class=code-quote>"Start First Draft"</span>&gt;</span>
<span class=code-tag>&lt;results&gt;</span>
<span class=code-tag>&lt;unconditional-result old-status=<span class=code-quote>"Finished"</span>
status=<span class=code-quote>"Underway"</span> step=<span class=code-quote>"1"</span>/&gt;</span>
<span class=code-tag>&lt;/results&gt;</span>
<span class=code-tag>&lt;/action&gt;</span>
<span class=code-tag>&lt;action id=<span class=code-quote>"2"</span> name=<span class=code-quote>"Finish First Draft"</span>&gt;</span>
<span class=code-tag>&lt;results&gt;</span>
<span class=code-tag>&lt;unconditional-result old-status=<span class=code-quote>"Finished"</span>
status=<span class=code-quote>"Queued"</span> step=<span class=code-quote>"2"</span>/&gt;</span>
<span class=code-tag>&lt;/results&gt;</span>
<span class=code-tag>&lt;/action&gt;</span>
<span class=code-tag>&lt;/actions&gt;</span>
<span class=code-tag>&lt;/step&gt;</span>
<span class=code-tag>&lt;step id=<span class=code-quote>"2"</span> name=<span class=code-quote>"finished"</span> /&gt;</span></pre>
</div>
</div>
<p>这样我们就定义了2个动作，old-status属性是用来指明当前步骤完成以后的状态是什么，在大多数的应用中，通常用"Finished"表示。</p>
<p>上面定义的这2个动作是没有任何限制的。比如，一个用户可以调用action 2而不用先调用action 1。很明显的，我们如果没有开始撰写草稿，是不可能去完成一个草稿的。同样的，上面的定义也允许你开始撰写草稿多次，这也是毫无意义的。我们也没有做任何的处理去限制其他用户完成别人的草稿。这些都应该需要想办法避免。</p>
<p>让我们来一次解决这些问题。首先，我们需要指定只有工作流的状态为&#8220;Queued&#8221;的时候，一个caller （调用者）才能开始撰写草稿的动作。这样就可以阻止其他用户多次调用开始撰写草稿的动作。我们需要指定动作的约束，约束是由Condition（条件）组成。</p>
<h3>条件</h3>
<p>OSWorkflow 有很多有用的内置条件可以使用。在此相关的条件是&#8220;StatusCondition - 状态条件&#8221;。 条件也可以接受参数，参数的名称通常被定义在javadocs里（如果是使用Java Class实现的条件的话）。在这个例子里面，状态条件接受一个名为&#8220;status&#8221;的参数，指明了需要检查的状态条件。我们可以从下面的xml定义里面清楚的理解：</p>
<div class=code>
<div class=codeContent>
<pre class=code-xml><span class=code-tag>&lt;action id=<span class=code-quote>"1"</span> name=<span class=code-quote>"Start First Draft"</span>&gt;</span>
<span class=code-tag>&lt;restrict-to&gt;</span>
<span class=code-tag>&lt;conditions&gt;</span>
<span class=code-tag>&lt;condition type=<span class=code-quote>"class"</span>&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"class.name"</span>&gt;</span>
com.opensymphony.workflow.util.StatusCondition<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"status"</span>&gt;</span>Queued<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;/condition&gt;</span>
<span class=code-tag>&lt;/conditions&gt;</span>
<span class=code-tag>&lt;/restrict-to&gt;</span>
<span class=code-tag>&lt;results&gt;</span>
<span class=code-tag>&lt;unconditional-result old-status=<span class=code-quote>"Finished"</span> status=<span class=code-quote>"Underway"</span> step=<span class=code-quote>"1"</span>/&gt;</span>
<span class=code-tag>&lt;/results&gt;</span>
<span class=code-tag>&lt;/action&gt;</span></pre>
</div>
</div>
<p>希望对于条件的理解现在已经清楚了。上面的条件定义保证了动作1只能在当前状态为&#8220;Queued&#8221;的时候才能被调用，也就是说在初始化动作被调用以后。</p>
<h3>函数</h3>
<p>接下来，我们想在一个用户开始撰写草稿以后，设置他为&#8220;owner&#8221;。为了达到这样的目的，我们需要做2件事情：</p>
<p>1) 通过一个函数设置&#8220;caller&#8221;变量在当前的环境设置里。<br>2) 根据&#8220;caller&#8221;变量来设置&#8220;owner&#8221;属性。</p>
<p>函数是OSWorkflow的一个功能强大的特性。函数基本上是一个在工作流程中的工作单位，他不会影响到流程本身。举例来说，你可能有一个&#8220;SendEmail&#8221;的函数，用来在某些特定的流程流转发生时来发送email提醒。</p>
<p>函数也可以用来添加变量到当前的环境设置里。变量是一个指定名称的对象，可以用来在工作流中被以后的函数或者脚本使用。</p>
<p>OSWorkflow提供了一些内置的常用函数。其中一个称为&#8220;Caller&#8221;，这个函数会获得当前调用工作流的用户，并把它放入一个名为&#8220;caller&#8221;的字符型变量中。</p>
<p>因为我们需要追踪是哪个用户开始了编写草稿，所以可以使用这个函数来修改我们的动作定义：</p>
<div class=code>
<div class=codeContent>
<pre class=code-xml><span class=code-tag>&lt;action id=<span class=code-quote>"1"</span> name=<span class=code-quote>"Start First Draft"</span>&gt;</span>
<span class=code-tag>&lt;pre-functions&gt;</span>
<span class=code-tag>&lt;function type=<span class=code-quote>"class"</span>&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"class.name"</span>&gt;</span>
com.opensymphony.workflow.util.Caller<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;/function&gt;</span>
<span class=code-tag>&lt;/pre-functions&gt;</span>
<span class=code-tag>&lt;results&gt;</span>
&lt;unconditional-result old-status=<span class=code-quote>"Finished"</span>status=<span class=code-quote>"Underway"</span>
step=<span class=code-quote>"1"</span> owner=<span class=code-quote>"${caller}"</span>/&gt;
<span class=code-tag>&lt;/results&gt;</span>
<span class=code-tag>&lt;/action&gt;</span></pre>
</div>
</div>
<p>h3 组合起来<br>把这些概念都组合起来，这样我们就有了动作1：</p>
<div class=code>
<div class=codeContent>
<pre class=code-xml><span class=code-tag>&lt;action id=<span class=code-quote>"1"</span> name=<span class=code-quote>"Start First Draft"</span>&gt;</span>
<span class=code-tag>&lt;restrict-to&gt;</span>
<span class=code-tag>&lt;conditions&gt;</span>
<span class=code-tag>&lt;condition type=<span class=code-quote>"class"</span>&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"class.name"</span>&gt;</span>
com.opensymphony.workflow.util.StatusCondition
<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"status"</span>&gt;</span>Queued<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;/condition&gt;</span>
<span class=code-tag>&lt;/conditions&gt;</span>
<span class=code-tag>&lt;/restrict-to&gt;</span>
<span class=code-tag>&lt;pre-functions&gt;</span>
<span class=code-tag>&lt;function type=<span class=code-quote>"class"</span>&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"class.name"</span>&gt;</span>
com.opensymphony.workflow.util.Caller
<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;/function&gt;</span>
<span class=code-tag>&lt;/pre-functions&gt;</span>
<span class=code-tag>&lt;results&gt;</span>
&lt;unconditional-result old-status=<span class=code-quote>"Finished"</span>status=<span class=code-quote>"Underway"</span>
step=<span class=code-quote>"1"</span>  owner=<span class=code-quote>"${caller}"</span>/&gt;
<span class=code-tag>&lt;/results&gt;</span>
<span class=code-tag>&lt;/action&gt;</span></pre>
</div>
</div>
<p>我们使用类似想法来设置动作2：</p>
<div class=code>
<div class=codeContent>
<pre class=code-xml><span class=code-tag>&lt;action id=<span class=code-quote>"2"</span> name=<span class=code-quote>"Finish First Draft"</span>&gt;</span>
<span class=code-tag>&lt;restrict-to&gt;</span>
<span class=code-tag>&lt;conditions type=<span class=code-quote>"AND"</span>&gt;</span>
<span class=code-tag>&lt;condition type=<span class=code-quote>"class"</span>&gt;</span>
&lt;arg name=<span class=code-quote>"class.name"</span>&gt;
com.opensymphony.workflow.util.StatusCondition
<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"status"</span>&gt;</span>Underway<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;/condition&gt;</span>
<span class=code-tag>&lt;condition type=<span class=code-quote>"class"</span>&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"class.name"</span>&gt;</span>
com.opensymphony.workflow.util.AllowOwnerOnlyCondition
<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;/condition&gt;</span>
<span class=code-tag>&lt;/conditions&gt;</span>
<span class=code-tag>&lt;/restrict-to&gt;</span>
<span class=code-tag>&lt;results&gt;</span>
<span class=code-tag>&lt;unconditional-result old-status=<span class=code-quote>"Finished"</span> status=<span class=code-quote>"Queued"</span> step=<span class=code-quote>"2"</span>/&gt;</span>
<span class=code-tag>&lt;/results&gt;</span>
<span class=code-tag>&lt;/action&gt;</span></pre>
</div>
</div>
<p>在这里我们指定了一个新的条件：&#8220;allow owner only&#8221;。这样能够保证只有开始撰写这份草稿的用户才能完成它。而状态条件确保了只有在&#8220;Underway&#8221;状态下的流程才能调用&#8220;finish first draft&#8221;动作。</p>
<p>把他们组合在一起，我们就有了第一个流程定义：</p>
<div class=code>
<div class=codeContent>
<pre class=code-xml><span class=code-tag>&lt;?xml version=<span class=code-quote>"1.0"</span> encoding=<span class=code-quote>"UTF-8"</span>?&gt;</span>
&lt;!DOCTYPE workflow PUBLIC
<span class=code-quote>"-//OpenSymphony Group//DTD OSWorkflow 2.7//EN"</span>
<span class=code-quote>"http://www.opensymphony.com/osworkflow/workflow_2_7.dtd"</span>&gt;
<span class=code-tag>&lt;workflow&gt;</span>
<span class=code-tag>&lt;initial-actions&gt;</span>
<span class=code-tag>&lt;action id=<span class=code-quote>"1"</span> name=<span class=code-quote>"Start Workflow"</span>&gt;</span>
<span class=code-tag>&lt;results&gt;</span>
<span class=code-tag>&lt;unconditional-result old-status=<span class=code-quote>"Finished"</span>
status=<span class=code-quote>"Queued"</span> step=<span class=code-quote>"1"</span>/&gt;</span>
<span class=code-tag>&lt;/results&gt;</span>
<span class=code-tag>&lt;/action&gt;</span>
<span class=code-tag>&lt;/initial-actions&gt;</span>
<span class=code-tag>&lt;steps&gt;</span>
<span class=code-tag>&lt;step id=<span class=code-quote>"1"</span> name=<span class=code-quote>"First Draft"</span>&gt;</span>
<span class=code-tag>&lt;actions&gt;</span>
<span class=code-tag>&lt;action id=<span class=code-quote>"1"</span> name=<span class=code-quote>"Start First Draft"</span>&gt;</span>
<span class=code-tag>&lt;restrict-to&gt;</span>
<span class=code-tag>&lt;conditions&gt;</span>
<span class=code-tag>&lt;condition type=<span class=code-quote>"class"</span>&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"class.name"</span>&gt;</span>
com.opensymphony.workflow.util.StatusCondition
<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"status"</span>&gt;</span>Queued<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;/condition&gt;</span>
<span class=code-tag>&lt;/conditions&gt;</span>
<span class=code-tag>&lt;/restrict-to&gt;</span>
<span class=code-tag>&lt;pre-functions&gt;</span>
<span class=code-tag>&lt;function type=<span class=code-quote>"class"</span>&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"class.name"</span>&gt;</span>
com.opensymphony.workflow.util.Caller
<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;/function&gt;</span>
<span class=code-tag>&lt;/pre-functions&gt;</span>
<span class=code-tag>&lt;results&gt;</span>
&lt;unconditional-result old-status=<span class=code-quote>"Finished"</span> status=<span class=code-quote>"Underway"</span>
step=<span class=code-quote>"1"</span>  owner=<span class=code-quote>"${caller}"</span>/&gt;
<span class=code-tag>&lt;/results&gt;</span>
<span class=code-tag>&lt;/action&gt;</span>
<span class=code-tag>&lt;action id=<span class=code-quote>"2"</span> name=<span class=code-quote>"Finish First Draft"</span>&gt;</span>
<span class=code-tag>&lt;restrict-to&gt;</span>
<span class=code-tag>&lt;conditions type=<span class=code-quote>"AND"</span>&gt;</span>
<span class=code-tag>&lt;condition type=<span class=code-quote>"class"</span>&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"class.name"</span>&gt;</span>
com.opensymphony.workflow.util.StatusCondition
<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"status"</span>&gt;</span>Underway<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;/condition&gt;</span>
<span class=code-tag>&lt;condition type=<span class=code-quote>"class"</span>&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"class.name"</span>&gt;</span>
com.opensymphony.workflow.util.
AllowOwnerOnlyCondition
<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;/condition&gt;</span>
<span class=code-tag>&lt;/conditions&gt;</span>
<span class=code-tag>&lt;/restrict-to&gt;</span>
<span class=code-tag>&lt;results&gt;</span>
<span class=code-tag>&lt;unconditional-result old-status=<span class=code-quote>"Finished"</span>
status=<span class=code-quote>"Queued"</span> step=<span class=code-quote>"2"</span>/&gt;</span>
<span class=code-tag>&lt;/results&gt;</span>
<span class=code-tag>&lt;/action&gt;</span>
<span class=code-tag>&lt;/actions&gt;</span>
<span class=code-tag>&lt;/step&gt;</span>
<span class=code-tag>&lt;step id=<span class=code-quote>"2"</span> name=<span class=code-quote>"finished"</span> /&gt;</span>
<span class=code-tag>&lt;/steps&gt;</span>
<span class=code-tag>&lt;/workflow&gt;</span></pre>
</div>
</div>
<p>现在这个工作流的定义已经完整了，让我们来测试和检查它的运行。</p>
<h1><a name=2></a>2. Testing your workflow</h1>
<p>现在我们已经完成了一个完整的工作流定义，下一步是检验它是否按照我们预想的方式执行。</p>
<p>在一个快速开发环境中，最简单的方法就是写一个测试案例。通过测试案例调用工作流，根据校验结果和捕捉可能发生的错误，来保证流程定义的正确性。</p>
<p>我们假设你已经熟悉Junit和了解怎样编写测试案例。如果你对这些知识还不了解的话，可以去JUnit的网站查找、阅读相关文档。编写测试案例会成为你的一个非常有用的工具。</p>
<p>在开始载入流程定义、调用动作以前，我们需要配置OSWorkflow的数据存储方式和定义文件的位置等。</p>
<h3>配置 osworkflow.xml</h3>
<p>我们需要创建的第一个文件是 <em>osworkflow.xml</em>。子：</p>
<div class=code>
<div class=codeContent>
<pre class=code-xml><span class=code-tag>&lt;osworkflow&gt;</span>
<span class=code-tag>&lt;persistence class=<span class=code-quote>"com.opensymphony.workflow.
spi.memory.MemoryWorkflowStore"</span>/&gt;</span>
<span class=code-tag>&lt;factory class=<span class=code-quote>"com.opensymphony.workflow.loader.XMLWorkflowFactory"</span>&gt;</span>
<span class=code-tag>&lt;property key=<span class=code-quote>"resource"</span> value=<span class=code-quote>"workflows.xml"</span> /&gt;</span>
<span class=code-tag>&lt;/factory&gt;</span>
<span class=code-tag>&lt;/osworkflow&gt;</span></pre>
</div>
</div>
<p>这个例子指明了我们准备使用内存 (MemoryWorkflowStore) 来保存流程数据。这样可以减少设置数据库的相关信息，减少出问题的可能性。用内存持久化对于测试来说是非常方便的。</p>
<h3>Workflow factories</h3>
<p>上面的配置文件还指明了我们工作流工厂（XMLWorkflowFactory），工作流工厂的主要功能是管理流程定义文件，包括读取定义文件和修改定义文件的功能。通过'resource'这个属性指明了采用通过从classpath中读取流程定义文件的方式，按照这个定义，接下来我们需要在classpath中创建一个名为workflows.xml的文件。</p>
<p>workflows.xml 的内容：</p>
<div class=code>
<div class=codeContent>
<pre class=code-xml><span class=code-tag>&lt;workflows&gt;</span>
<span class=code-tag>&lt;workflow name=<span class=code-quote>"mytest"</span> type=<span class=code-quote>"resource"</span> location=<span class=code-quote>"myworkflow.xml"</span>/&gt;</span>
<span class=code-tag>&lt;/workflows&gt;</span></pre>
</div>
</div>
<p>我们把 <em>myworkflow.xml</em> 和workflows.xml放在同一目录，这样它就能够被工作流工厂读取了。</p>
<p>这样就完成了配置，接下来是初始化一个流程并调用它。</p>
<h3>Initialising OSWorkflow</h3>
<p>OSWorkflow 的调用模式相当简单：通过一个主要的接口来执行大部分操作。这个接口就是 <em>Workflow</em> interface，及其扩展 <em>AbstractWorkflow</em> 的实现，例如EJBWorkflow 和 SOAPWorkflow. 为了简单起见，我们使用最基本的一种： BasicWorkflow。</p>
<p>首先，我们来创建Workflow。在实际项目中，这个对象应该被放在一个全局的位置上以供重用，因为每次都创建一个新的Workflow对象是需要耗费比较昂贵的系统资源。在这里的例子，我们采用BasicWorkflow，它的构建器由一个当前调用者的用户名构成，当然我们很少看到单用户的工作流应用，可以参考其他的Workflow实现有不同的方式去获得当前调用者。</p>
<p>为了简单起见，我们采用BasicWorkflow来创建一个单一的用户模式，避免编写其他获取用户方法的麻烦。</p>
<p>这样我们来创建一个'testuser'调用的workflow:</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>Workflow workflow = <span class=code-keyword>new</span> BasicWorkflow(<span class=code-quote>"testuser"</span>);</pre>
</div>
</div>
<p>下一步是提供配置文件，在大多数情况下，只是简单的传递一个DefaultConfiguration实例：</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>DefaultConfiguration config = <span class=code-keyword>new</span> DefaultConfiguration();
workflow.setConfiguration(config);</pre>
</div>
</div>
<p>现在我们已经创建并且配置好了一个workflow，接下来就是开始调用它了。</p>
<h3>启动和进行一个工作流程</h3>
<p>首先我们需要调用initialize 方法来启动一个工作流程，这个方法有3个参数，workflow name （定义在workflows.xml里，通过workflow factory处理）, action ID （我们要调用的初始化动作的ID），和初始化变量。 因为在例子里面不需初始化变量，所以我们只是传递一个null，</p>
<div class=code>
<div class=codeContent>
<pre class=code-java><span class=code-object>long</span> workflowId = workflow.initialize(<span class=code-quote>"mytest"</span>, 1, <span class=code-keyword>null</span>);</pre>
</div>
</div>
<p>我们现在已经有了一个工作流实例，返回的workflowId可以在后续的操作中来代表这个实例。这个参数会在Workflow interface的绝大部分方法中用到。</p>
<h3>检验工作流</h3>
<p>现在让我们来检验启动的工作流实例是否按照我们所预期的那样运行。根据流程定义，我们期望的当前步骤是第一步，而且应该可以执行第一个动作（开始编写草稿）。</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>Collection currentSteps = workflow.getCurrentSteps
(workflowId);
<span class=code-comment>//校验只有一个当前步骤
</span>assertEquals(<span class=code-quote>"Unexpected number of current steps"</span>,
1, currentSteps.size());
<span class=code-comment>//校验这个步骤是1
</span>Step currentStep = (Step)currentSteps.iterator().next();
assertEquals(<span class=code-quote>"Unexpected current step"</span>, 1,
currentStep.getStepId());
<span class=code-object>int</span>[] availableActions =
workflow.getAvailableActions(workflowId);
<span class=code-comment>//校验只有一个可执行的动作
</span>assertEquals(<span class=code-quote>"Unexpected number of available actions"</span>, 1,
availableActions.length);
<span class=code-comment>//校验这个步骤是1
</span>assertEquals(<span class=code-quote>"Unexpected available action"</span>, 1, availableActions[0]);</pre>
</div>
</div>
<h3>执行动作</h3>
<p>现在已经校验完了工作流实例正如我们所期望的到了第一个步骤，让我们来开始执行第一个动作：</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>workflow.doAction(workflowId, 1, <span class=code-keyword>null</span>);</pre>
</div>
</div>
<p>这是简单的调用第一个动作，工作流引擎根据指定的条件，改变状态到&#8216;Underway&#8217;，并且保持在当前步骤。</p>
<p>现在我们可以类似地调用第2个动作，它所需要的条件已经都满足了。</p>
<p>在调用完第2个动作以后，根据流程定义就没有可用的动作了，getAvailableActions将会返回一个空数组。</p>
<p>Congratulations, 你已经完成了一个工作流定义并且成功地调用了它。下一节我们将会讲解osworkflow一些更深入的概念。</p>
<a name=3></a>
<h1>3. Further descriptor concepts </h1>
<h3>定义条件和函数</h3>
<p>你也许已经注意到，到目前为止，我们定义的条件和函数类型都是&#8220;class&#8221;。这种类型的条件和函数接受一个参数：&#8220;class.name&#8221;，以此来指明一个实现FunctionProvider或Condition接口的完整类名。</p>
<p>在osworkflow里面也有一些其他内置的类型，包括beanshell，无状态的session bean，JNDI树上的函数等。我们在下面的例子里使用beanshell类型。</p>
<h3>Property sets</h3>
<p>我们可能需要在工作流的任意步骤持久化一些少量数据。在osworkflow里，这是通过OpenSymphony的PropertySet library来实现。一个PropertySet基本上是一个可以持久化的类型安全map，你可以添加任意的数据到propertyset（一个工作流实例对应一个propertyset），并在以后的流程中再读取这些数据。除非你特别指定操作，否则propertyset中的数据不会被清空或者被删除。任意的函数和条件都可以和propertyset交互，以beanshell script来说，可以在脚本上下文中用&#8220;propertyset&#8221;这个名字来获取。下面来看具体写法是怎么样的，让我们增加如下的代码在&#8220;Start First Draft&#8221;动作的pre-functions里面：</p>
<div class=code>
<div class=codeContent>
<pre class=code-xml><span class=code-tag>&lt;function type=<span class=code-quote>"beanshell"</span>&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"script"</span>&gt;</span>propertySet.setString(<span class=code-quote>"foo"</span>, <span class=code-quote>"bar"</span>)<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;/function&gt;</span></pre>
</div>
</div>
<p>这样我们就添加了一个持久化的属性&#8220;foo&#8221;，它的值是&#8220;bar&#8221;。这样在以后的流程中，我们就可以获得这个值了。</p>
<h3>Transient Map 临时变量</h3>
<p>另外一个和propertyset变量相对的概念是临时变量：&#8220;transientVars&#8221;。临时变量是一个简单的map，只是在当前的工作流调用的上下文内有效。它包括当前的工作流实例，工作流定义等对应值的引用。你可以通过FunctionProvider的javadoc来查看这个map有那些可用的key。</p>
<p>还记得我们在教程的第2部分传入的那个null吗？如果我们不传入null的话，那么这些输入数据将会被添加到临时变量的map里。</p>
<h3>inputs 输入</h3>
<p>每次调用workflow的动作时可以输入一个可选的map，可以在这个map里面包含供函数和条件使用的任何数据，它不会被持久化，只是一个简单的数据传递。</p>
<h3>Validators 校验器</h3>
<p>为了让工作流能够校验输入的数据，引入了校验器的概念。一个校验器和函数，条件的实现方式非常类似（比如，它可以是一个class，脚本，或者EJB）。在这个教程里面，我们将会定义一个校验器，在&#8220;finish first draft&#8221;这个步骤，校验用户输入的数据&#8220;working.title&#8221;不能超过30个字符。这个校验器看起来是这样的：</p>
<div class=code>
<div class=codeContent>
<pre class=code-java><span class=code-keyword>package</span> com.mycompany.validators;
<span class=code-keyword>public</span> class TitleValidator <span class=code-keyword>implements</span> Validator
{
<span class=code-keyword>public</span> void validate(Map transientVars, Map args,
PropertySet ps)
<span class=code-keyword>throws</span> InvalidInputException, WorkflowException
{
<span class=code-object>String</span> title =
(<span class=code-object>String</span>)transientVars.get(<span class=code-quote>"working.title"</span>);
<span class=code-keyword>if</span>(title == <span class=code-keyword>null</span>)
<span class=code-keyword>throw</span> <span class=code-keyword>new</span> InvalidInputException(<span class=code-quote>"Missing working.title"</span>);
<span class=code-keyword>if</span>(title.length() &gt; 30)
<span class=code-keyword>throw</span> <span class=code-keyword>new</span> InvalidInputException(<span class=code-quote>"Working title too <span class=code-object>long</span>"</span>);
}
}</pre>
</div>
</div>
<p>然后通过在流程定义文件添加validators元素，就可以登记这个校验器了：</p>
<div class=code>
<div class=codeContent>
<pre class=code-xml><span class=code-tag>&lt;validators&gt;</span>
<span class=code-tag>&lt;validator type=<span class=code-quote>"class"</span>&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"class.name"</span>&gt;</span>
com.mycompany.validators.TitleValidator
<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;/validator&gt;</span>
<span class=code-tag>&lt;/validators&gt;</span></pre>
</div>
</div>
<p>这样，当我们执行动作2的时候，这个校验器将会被调用，并且检验我们的输入。这样在测试代码里面，如果加上：</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>Map inputs = <span class=code-keyword>new</span> HashMap();
inputs.put(<span class=code-quote>"working.title"</span>,
<span class=code-quote>"the quick brown fox jumped over the lazy dog,"</span> +
<span class=code-quote>" thus making <span class=code-keyword>this</span> a very <span class=code-object>long</span> title"</span>);
workflow.doAction(workflowId, 2, inputs);</pre>
</div>
</div>
<p>我们将会得到一个InvalidInputException，这个动作将不会被执行。减少输入的title字符，将会让这个动作成功执行。</p>
<p>我们已经介绍了输入和校验，下面来看看寄存器。</p>
<h3>Registers 寄存器</h3>
<p>寄存器是一个工作流的全局变量。和propertyset类似，它可以在工作流实例的任意地方被获取。和propertyset不同的是，它不是一个持久化的数据，而是每次调用时都需要重新计算的数据。</p>
<p>它可以被用在什么地方呢？在我们的文档管理系统里面，如果定义了一个&#8220;document&#8221;的寄存器，那么对于函数、条件、脚本来说就是非常有用的：可以用它来获得正在被编辑的文档。</p>
<p>寄存器地值会被放在临时变量（transientVars map）里，这样能够在任意地方获得它。</p>
<p>定义一个寄存器和函数、条件的一个重要区别是，它并不是依靠特定的调用（不用关心当前的步骤，或者是输入数据，它只是简单地暴露一些数据而已），所以它不用临时变量里的值。</p>
<p>寄存器必须实现Register接口，并且被定义在流程定义文件的头部，在初始化动作之前。</p>
<p>举例来说，我们将会使用一个osworkflow内置的寄存器:LogRegister。这个寄存器简单的添加一个&#8220;log&#8221;变量，能够让你使用Jakarta的commons-logging输出日志信息。它的好处是会在每条信息前添加工作流实例的ID。</p>
<div class=code>
<div class=codeContent>
<pre class=code-xml><span class=code-tag>&lt;registers&gt;</span>
<span class=code-tag>&lt;register type=<span class=code-quote>"class"</span> variable-name=<span class=code-quote>"log"</span>&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"class.name"</span>&gt;</span>
com.opensymphony.workflow.util.LogRegister
<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"addInstanceId"</span>&gt;</span>true<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"Category"</span>&gt;</span>workflow<span class=code-tag>&lt;/arg&gt;</span>
<span class=code-tag>&lt;/register&gt;</span>
<span class=code-tag>&lt;/registers&gt;</span></pre>
</div>
</div>
<p>这样我们定义了一个可用的&#8220;log&#8221;变量，可以通过其他的pre－function的脚本里面使用它：</p>
<div class=code>
<div class=codeContent>
<pre class=code-xml><span class=code-tag>&lt;function type=<span class=code-quote>"beanshell"</span>&gt;</span>
<span class=code-tag>&lt;arg name=<span class=code-quote>"script"</span>&gt;</span>transientVars.get(<span class=code-quote>"log"</span>).info(<span class=code-quote>"executing action 2"</span>)<span class=code-tag>
&lt;/arg&gt;</span>
<span class=code-tag>&lt;/function&gt;</span></pre>
</div>
</div>
<p>日志输出将会在前面添加工作流实例的ID</p>
<h3>结论</h3>
<p>这个教程的目的是希望可以阐明一些主要的osworkflow概念。你还可以通过API和流程定义格式去获取更多的信息。有一些更高级的特性没有在此提到，比如splits 分支、joins 连接, nested conditions 复合条件、auto stpes 自动步骤等等。你可以通过阅读手册来获得更进一步的理解。</p>
<img src ="http://www.blogjava.net/livery/aggbug/127761.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2007-07-03 10:36 <a href="http://www.blogjava.net/livery/articles/127761.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OSWorkflow结构(转载)</title><link>http://www.blogjava.net/livery/articles/127760.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Tue, 03 Jul 2007 02:34:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/127760.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/127760.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/127760.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/127760.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/127760.html</trackback:ping><description><![CDATA[OSWorkFlow分析<br>3.&nbsp;&nbsp;&nbsp;&nbsp;OSWorkFlow核心概念<br>3.1.&nbsp;&nbsp;&nbsp;&nbsp;概念定义<br>&nbsp;<br>步骤（Step）<br>&nbsp;&nbsp;&nbsp;&nbsp;一个&nbsp;Step&nbsp;描述的是工作流所处的位置。可能从一个&nbsp;Step&nbsp;Transtion（流转）到另外一个&nbsp;Step，或者也可以在同一个&nbsp;Step&nbsp;内流转（因为&nbsp;Step&nbsp;可以通&nbsp;Status&nbsp;来细分，形成多个State）。一个流程里面可以多个Step。<br><br>状态（Status）<br>&nbsp;&nbsp;&nbsp;&nbsp;工作流&nbsp;Status&nbsp;是用来描述工作流程中具体Step（步骤）状态的字符串。OSWorkflow&nbsp;的有&nbsp;Underway（进行中）、Queued（等候处理中）、Finished（完成）三种&nbsp;Status。一个实际State（状态）真正是由两部分组成：State&nbsp;=&nbsp;(Step&nbsp;+&nbsp;Status)&nbsp;。<br><br>流转（Transtion）<br>&nbsp;&nbsp;&nbsp;&nbsp;一个State到另一个State的转移。<br><br>动作（Action）<br>&nbsp;&nbsp;&nbsp;&nbsp;Action&nbsp;触发了发生在&nbsp;Step&nbsp;内或&nbsp;Step&nbsp;间的流转，或者说是基于&nbsp;State&nbsp;的流转。一个&nbsp;step&nbsp;里面可以有多个Action。Action&nbsp;和Step&nbsp;之间的关系是，Step&nbsp;说明&#8220;在哪里&#8221;，Action&nbsp;说明&#8220;去哪里&#8221;。&nbsp;一个&nbsp;Action&nbsp;典型地由两部分组成：可以执行此Action（动作）的<br>Condition（条件），以及执行此动作后的&nbsp;Result（结果）。&nbsp;&nbsp;&nbsp;&nbsp;<br><br>条件（Condition）<br>类似于逻辑判断，可包含&#8220;AND&#8221;和&#8220;OR&#8221;逻辑。比如一个请假流程中的&#8220;本部门审批阶段&#8221;，该阶段利用&#8220;AND&#8221;逻辑，判断流程状态是否为等候处理中，以及审批者是否为本部门主管。<br><br>结果（Result）<br>Result&nbsp;代表执行Action（动作）后的结果，指向新的&nbsp;Step&nbsp;及其&nbsp;Step&nbsp;Status，也可能进入&nbsp;Split&nbsp;或者&nbsp;Join。Result&nbsp;分为两种，&nbsp;Contidional-Result&nbsp;（有条件结果），只有条件为真时才使用该结果，和&nbsp;Unconditional-Result（无条件结果），当条件不满足或没有条件时使用该结果。<br><br>分离/连接（Split/Join）<br>流程的切分和融合。很简单的概念，Split&nbsp;可以提供多个&nbsp;Result（结果）；Join&nbsp;则判断多个&nbsp;Current&nbsp;Step&nbsp;的态提供一个&nbsp;Result（结果）。<br>3.2.&nbsp;&nbsp;&nbsp;&nbsp;步骤、状态和动作(Step,&nbsp;Status,&nbsp;and&nbsp;Action)<br>工作流要描述步骤(Step)、步骤的状态(Status)、各个步骤之间的关系以及执行各个步骤的条件和权限，每个步骤中可以含有一个或多个动作(Action)，动作将会使一个步骤的状态发生改变。<br><br>对于一个执行的工作流来讲，步骤的切换是不可避免的。一个工作流在某一时刻会有一个或多个当前步骤，每个当前步骤都有一个状态值，当前步骤的状态值组成了工作流实例的状态值。一旦完成了一个步骤，那么这个步骤将不再是当前步骤（而是切换到一个新的步骤），通常一个新的当前步骤将随之建立起来，以保证工作流继续执行。完成了的步骤的最终状态值是用Old-Status属性指定的，这个状态值的设定将发生在切换到其他步骤之前。Old-Status的值可以是任意的，但在一般情况下，我们设置为Finished。<br><br>切换本身是一个动作（Action）的执行结果。每个步骤可以含有多个动作，究竟要载入哪个动作是由最终用户、外部事件或者Tiggerd的自动调用决定的。随着动作的完成，一个特定的步骤切换也将发生。动作可以被限制在用户、用户组或当前状态。每一个动作都必须包含一个Unconditional&nbsp;Result和0个或多个Conditional&nbsp;Results。<br><br>所以，总体来说，一个工作流由多个步骤组成。每个步骤有一个当前状态（例如：Queued,&nbsp;Underway&nbsp;or&nbsp;Finished），一个步骤包含多个动作。每个步骤含有多个可以执行的动作。每个动作都有执行的条件，也有要执行的函数。动作包含有可以改变状态和当前工作流步骤的results。<br>3.3.&nbsp;&nbsp;&nbsp;&nbsp;结果、分支和连接(Results,&nbsp;Joins,&nbsp;and&nbsp;Splits)<br>3.3.1.&nbsp;&nbsp;&nbsp;&nbsp;无条件结果(Unconditional&nbsp;Result)<br>对于每一个动作来讲，必须存在一个Unconditional&nbsp;Result。一个result是一系列指令，这些指令将告诉OSWorkFlow下一个任务要做什么。这包括使工作流从一个状态切换到另一个状态。<br>3.3.2.&nbsp;&nbsp;&nbsp;&nbsp;有条件结果(Conditional&nbsp;Result)<br>Conditional&nbsp;Result是Unconditional&nbsp;Result的一个扩展。它需要一个或多个Condition子标签。第一个为true的Conditional（使用AND或OR类型），会指明发生切换的步骤，这个切换步骤的发生是由于某个用户执行了某个动作的结果导致的。<br>3.3.3.&nbsp;&nbsp;&nbsp;&nbsp;三种不同的Results(conditional&nbsp;or&nbsp;unconditional)<br>一个新的、单一的步骤和状态的组合。<br>一个分裂成两个或多个步骤和状态的组合。<br>将这个和其他的切换组合成一个新的单一的步骤和状态的组合。<br>每种不同的result对应了不同的xml描述，你可以阅读<a href="http://www.opensymphony.com/osworkflow/workflow_2_7.dtd"><u><font color=#0000ff>http://www.opensymphony.com/osworkflow/workflow_2_7.dtd</font></u></a>，获取更多的信息。<br>注意：通常，一个split或一个join不会再导致一个split&nbsp;或&nbsp;join的发生。<br>3.4.&nbsp;&nbsp;&nbsp;&nbsp;自动步骤(Auto&nbsp;actions)<br>有的时候，我们需要一些动作可以基于一些条件自动地执行。为了达到这个目的，你可以在action中加入auto="true"属性。流程将考察这个动作的条件和限制，如果条件符合，那么将执行这个动作。&nbsp;Auto&nbsp;action是由当前的调用者执行的，所以将对该动作的调用者执行权限检查。<br>3.5.&nbsp;&nbsp;&nbsp;&nbsp;整合抽象实例(Integrating&nbsp;with&nbsp;Abstract&nbsp;Entities)<br>建议在你的核心实体中，例如"Document"&nbsp;或&nbsp;"Order"，在内部创建一个新的属性：workflowId。这样，当新的"Document"&nbsp;或&nbsp;"Order"被创建的时候，它能够和一个workflow实例关联起来。那么，你的代码可以通过OSWorkflow&nbsp;API查找到这个workflow实例并且得到这个workflow的信息和动作。<br>3.6.&nbsp;&nbsp;&nbsp;&nbsp;工作流实例状态(Workflow&nbsp;Instance&nbsp;State)<br>有的时候，为整个workflow实例指定一个状态是很有帮助的，它独立于流程的执行步骤。OSWorkflow提供一些workflow实例中可以包含的"meta-states"。这些"meta-states"可以是CREATED,&nbsp;ACTIVATED,&nbsp;SUSPENDED,&nbsp;KILLED&nbsp;和&nbsp;COMPLETED。当一个工作流实例被创建的时候，它将处于CREATED状态。然后，只要一个动作被执行，它就会自动的变成ACTIVATED状态。如果调用者没有明确地改变实例的状态，工作流将一直保持这个状态直到工作流结束。当工作流不可能再执行任何其他的动作的时候，工作流将自动的变成COMPLETED状态。<br><br>然而，当工作流处于ACTIVATED状态的时候，调用者可以终止或挂起这个工作流（设置工作流的状态为KILLED&nbsp;或&nbsp;SUSPENDED）。一个终止了的工作流将不能再执行任何动作，而且将永远保持着终止状态。一个被挂起了的工作流会被冻结，他也不能执行任何的动作，除非它的状态再变成ACTIVATED。<br>4.&nbsp;&nbsp;&nbsp;&nbsp;OSWorkFlow包用途分析及代码片断<br>4.1.&nbsp;&nbsp;&nbsp;&nbsp;com.opensymphony.workflow<br>该包为整个OSWorkflow&nbsp;引擎提供核心接口。例如&nbsp;com.opensymphony.workflow.Workflow&nbsp;接口，可以说，实际开发中的大部分工作都是围绕该接口展开的，该接口有&nbsp;BasicWorkflow、EJBWorkflow、OfbizWorkflow&nbsp;三个实现类。<br>4.2.&nbsp;&nbsp;&nbsp;&nbsp;com.opensymphony.workflow.basic<br>该包有两个类，BasicWorkflow&nbsp;与&nbsp;BasicWorkflowContext。BasicWorkflow&nbsp;不支持事务，尽管依赖持久实现，事务也不能包裹它。BasicWorkflowContext&nbsp;在实际开发中很少使用。<br><br>public&nbsp;void&nbsp;setWorkflow(int&nbsp;userId)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;Workflow&nbsp;workflow&nbsp;=&nbsp;new&nbsp;BasicWorkflow(Integer.toString(userId));<br>}<br><br>4.3.&nbsp;&nbsp;&nbsp;&nbsp;com.opensymphony.workflow.config<br>该包有一个接口和两个该接口的实现类。在&nbsp;OSWorkflow&nbsp;2.7&nbsp;以前，状态由多个地方的静态字段维护，这种方式很方便，但是有很多缺陷和约束。最主要的缺点是无法通过不同配置运行多个&nbsp;OSWorkflow&nbsp;实例。实现类&nbsp;DefaultConfiguration&nbsp;用于一般的配置文件载入。而&nbsp;SpringConfiguration&nbsp;则是让&nbsp;Spring&nbsp;容器管理配置信息。<br><br>public&nbsp;void&nbsp;setWorkflow(int&nbsp;userId)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;Workflow&nbsp;workflow&nbsp;=&nbsp;new&nbsp;BasicWorkflow(Integer.toString(userId));<br>}<br>4.4.&nbsp;&nbsp;&nbsp;&nbsp;com.opensymphony.workflow.ejb<br>&nbsp;&nbsp;&nbsp;&nbsp;该包有两个接口&nbsp;WorkflowHome&nbsp;和&nbsp;WorkflowRemote。该包的若干类中，最重要的是&nbsp;EJBWorkflow，该类和&nbsp;BasicWorkflow&nbsp;的作用一样，是&nbsp;OSWorkflow&nbsp;的核心，并利用&nbsp;EJB&nbsp;容器管理事务，也作为工作流&nbsp;session&nbsp;bean&nbsp;的包装器。<br>4.5.&nbsp;&nbsp;&nbsp;&nbsp;com.opensymphony.workflow.loader<br>该包有若干类，用得最多的是&nbsp;XxxxDescriptor，如果在工作流引擎运行时需要了解指定的动作、步骤的状态、名字，等信息时，这些描述符会起到很大作用。<br><br>public&nbsp;String&nbsp;findNameByStepId(int&nbsp;stepId,String&nbsp;wfName)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;WorkflowDescriptor&nbsp;wd&nbsp;=&nbsp;workflow.getWorkflowDescriptor(wfName);<br>&nbsp;&nbsp;&nbsp;&nbsp;StepDescriptor&nbsp;stepDes&nbsp;=&nbsp;wd.getStep(stepId);<br>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;stepDes.getName();<br>}<br>4.6.&nbsp;&nbsp;&nbsp;&nbsp;com.opensymphony.workflow.ofbiz<br>&nbsp;&nbsp;&nbsp;&nbsp;OfbizWorkflow&nbsp;和&nbsp;BasicWorkflow&nbsp;在很多方面非常相似，除了需要调用&nbsp;ofbiz&nbsp;的&nbsp;TransactionUtil&nbsp;来包装事务。<br>4.7.&nbsp;&nbsp;&nbsp;&nbsp;com.opensymphony.workflow.query<br>该包主要为查询而设计，但不是所有的工作流存储都支持查询。通常，Hibernate&nbsp;和&nbsp;JDBC&nbsp;都支持，而内存工作流存储不支持。值得注意的是&nbsp;Hibernate&nbsp;存储不支持混合型查询（例如，一个查询同时包含了&nbsp;history&nbsp;step&nbsp;上下文和&nbsp;current&nbsp;step&nbsp;上下文）。执行一个查询，需要创建&nbsp;WorkflowExpressionQuery&nbsp;实例，接着调用&nbsp;Workflow&nbsp;对象的&nbsp;query&nbsp;方法来得到最终查询结果。<br><br>public&nbsp;List&nbsp;queryDepAdmin(int&nbsp;userId,int&nbsp;type)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;int[]&nbsp;arr&nbsp;=&nbsp;getSubPerson(userId,type);<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;//构造表达式<br>&nbsp;&nbsp;&nbsp;&nbsp;Expression[]&nbsp;expressions&nbsp;=&nbsp;new&nbsp;Expression[1&nbsp;+&nbsp;arr.length];<br>&nbsp;&nbsp;&nbsp;&nbsp;Expression&nbsp;expStatus&nbsp;=&nbsp;new&nbsp;FieldExpression(FieldExpression.STATUS,<br>&nbsp;&nbsp;&nbsp;&nbsp;FieldExpression.CURRENT_STEPS,&nbsp;FieldExpression.EQUALS,&nbsp;"Queued");<br>&nbsp;&nbsp;&nbsp;&nbsp;expressions[0]&nbsp;=&nbsp;expStatus;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;arr.length;&nbsp;i++)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Expression&nbsp;expOwner&nbsp;=&nbsp;new&nbsp;FieldExpression(FieldExpression.OWNER,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FieldExpression.CURRENT_STEPS,&nbsp;FieldExpression.EQUALS,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Integer.toString(arr[i]));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expressions[i&nbsp;+&nbsp;1]&nbsp;=&nbsp;expOwner;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;//查询未完成流编号<br>&nbsp;&nbsp;&nbsp;&nbsp;List&nbsp;wfIdList&nbsp;=&nbsp;null;<br>&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WorkflowExpressionQuery&nbsp;query&nbsp;=&nbsp;new&nbsp;WorkflowExpressionQuery(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;NestedExpression(expressions,&nbsp;NestedExpression.AND));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wfIdList&nbsp;=&nbsp;workflow.query(query);<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br>4.8.&nbsp;&nbsp;&nbsp;&nbsp;com.opensymphony.workflow.soap<br>&nbsp;&nbsp;&nbsp;&nbsp;OSWorkflow&nbsp;通过&nbsp;SOAP&nbsp;来支持远端调用。这种调用借助&nbsp;WebMethods&nbsp;实现。<br>4.9.&nbsp;&nbsp;&nbsp;&nbsp;com.opensymphony.workflow.spi<br>该包可以说是&nbsp;OSWorkflow&nbsp;与持久层打交道的途径，如当前工作流的实体，其中包括：EJB、Hibernate、JDBC、Memory、Ofbiz、OJB、Prevayler。<br><br>HibernateWorkflowEntry&nbsp;hwfe&nbsp;=&nbsp;(HibernateWorkflowEntry)&nbsp;getHibernateTemplate()<br>&nbsp;&nbsp;&nbsp;&nbsp;.find("from&nbsp;HibernateWorkflowEntry&nbsp;where&nbsp;Id="<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;wfIdList.get(i)).get(0);<br>4.10.&nbsp;&nbsp;&nbsp;&nbsp;com.opensymphony.workflow.util<br>该包是&nbsp;OSWorkflow&nbsp;的工具包，包括了对&nbsp;BeanShell、BSF、EJB&nbsp;Local、EJB&nbsp;Remote、JNDI&nbsp;的支持。<br>5.&nbsp;&nbsp;&nbsp;&nbsp;OSWorkFlow表结构分析<br>5.1.&nbsp;&nbsp;&nbsp;&nbsp;OS_WFENTRY<br>工作流主表，存放工作流名称和状态<br><br>字段名&nbsp;&nbsp;&nbsp;&nbsp;数据类型&nbsp;&nbsp;&nbsp;&nbsp;说明<br>ID&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;自动编号<br>NAME&nbsp;&nbsp;&nbsp;&nbsp;VARCHAR2(20)&nbsp;&nbsp;&nbsp;&nbsp;工作流名称<br>STATE&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;工作流状态<br><br>5.2.&nbsp;&nbsp;&nbsp;&nbsp;OS_CURRENTSTEP<br>当前步骤表，存放当前正在进行步骤的数据<br><br>字段名&nbsp;&nbsp;&nbsp;&nbsp;数据类型&nbsp;&nbsp;&nbsp;&nbsp;说明<br>ID&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;自动编号<br>ENTRY_ID&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;工作流编号<br>STEP_ID&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;步骤编号<br>ACTION_ID&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;动作编号<br>OWNER&nbsp;&nbsp;&nbsp;&nbsp;VARCHAR2(20)&nbsp;&nbsp;&nbsp;&nbsp;步骤的所有者<br>START_DATE&nbsp;&nbsp;&nbsp;&nbsp;DATE&nbsp;&nbsp;&nbsp;&nbsp;开始时间<br>FINISH_DATE&nbsp;&nbsp;&nbsp;&nbsp;DATE&nbsp;&nbsp;&nbsp;&nbsp;结束时间<br>DUE_DATE&nbsp;&nbsp;&nbsp;&nbsp;DATE&nbsp;&nbsp;&nbsp;&nbsp;授权时间<br>STATUS&nbsp;&nbsp;&nbsp;&nbsp;VARCHAR2(20)&nbsp;&nbsp;&nbsp;&nbsp;状态<br>CALLER&nbsp;&nbsp;&nbsp;&nbsp;VARCHAR2(20)&nbsp;&nbsp;&nbsp;&nbsp;操作人员的帐号名称<br><br>5.3.&nbsp;&nbsp;&nbsp;&nbsp;OS_CURRENTSTEP_PREV<br>前步骤表，存放当前步骤和上一个步骤的关联数据<br><br>字段名&nbsp;&nbsp;&nbsp;&nbsp;数据类型&nbsp;&nbsp;&nbsp;&nbsp;说明<br>ID&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;当前步骤编号<br>PREVIOUS&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;前步骤编号<br><br>5.4.&nbsp;&nbsp;&nbsp;&nbsp;OS_HISTORYSTEP<br>历史步骤表，存放当前正在进行步骤的数据<br><br>字段名&nbsp;&nbsp;&nbsp;&nbsp;数据类型&nbsp;&nbsp;&nbsp;&nbsp;说明<br>ID&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;自动编号<br>ENTRY_ID&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;工作流编号<br>STEP_ID&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;步骤编号<br>ACTION_ID&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;动作编号<br>OWNER&nbsp;&nbsp;&nbsp;&nbsp;VARCHAR2(20)&nbsp;&nbsp;&nbsp;&nbsp;步骤的所有者<br>START_DATE&nbsp;&nbsp;&nbsp;&nbsp;DATE&nbsp;&nbsp;&nbsp;&nbsp;开始时间<br>FINISH_DATE&nbsp;&nbsp;&nbsp;&nbsp;DATE&nbsp;&nbsp;&nbsp;&nbsp;结束时间<br>DUE_DATE&nbsp;&nbsp;&nbsp;&nbsp;DATE&nbsp;&nbsp;&nbsp;&nbsp;授权时间<br>STATUS&nbsp;&nbsp;&nbsp;&nbsp;VARCHAR2(20)&nbsp;&nbsp;&nbsp;&nbsp;状态<br>CALLER&nbsp;&nbsp;&nbsp;&nbsp;VARCHAR2(20)&nbsp;&nbsp;&nbsp;&nbsp;操作人员的帐号名称<br><br>5.5.&nbsp;&nbsp;&nbsp;&nbsp;OS_HISTORYSTEP_PREV<br>前历史步骤表，存放历史步骤和上一个步骤的关联数据<br><br>字段名&nbsp;&nbsp;&nbsp;&nbsp;数据类型&nbsp;&nbsp;&nbsp;&nbsp;说明<br>ID&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;当前历史步骤编号<br>PREVIOUS&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;前历史步骤编号<br><br>5.6.&nbsp;&nbsp;&nbsp;&nbsp;OS_PROPERTYENTRY<br>属性表，存放临时变量<br><br>字段名&nbsp;&nbsp;&nbsp;&nbsp;数据类型&nbsp;&nbsp;&nbsp;&nbsp;说明<br>GLOBAL_KEY&nbsp;&nbsp;&nbsp;&nbsp;VARCHAR2(255)&nbsp;&nbsp;&nbsp;&nbsp;全局关键字<br>ITEM_KEY&nbsp;&nbsp;&nbsp;&nbsp;VARCHAR2(255)&nbsp;&nbsp;&nbsp;&nbsp;条目关键字<br>ITEM_TYPE&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;条目类型<br>STRING_VALUE&nbsp;&nbsp;&nbsp;&nbsp;VARCHAR2(255)&nbsp;&nbsp;&nbsp;&nbsp;字符值<br>DATE_VALUE&nbsp;&nbsp;&nbsp;&nbsp;DATE&nbsp;&nbsp;&nbsp;&nbsp;日期值<br>DATA_VALUE&nbsp;&nbsp;&nbsp;&nbsp;BLOB&nbsp;&nbsp;&nbsp;&nbsp;数据值<br>FLOAT_VALUE&nbsp;&nbsp;&nbsp;&nbsp;FLOAT&nbsp;&nbsp;&nbsp;&nbsp;浮点值<br>NUMBER_VALUE&nbsp;&nbsp;&nbsp;&nbsp;NUMBER&nbsp;&nbsp;&nbsp;&nbsp;数字值 
<img src ="http://www.blogjava.net/livery/aggbug/127760.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2007-07-03 10:34 <a href="http://www.blogjava.net/livery/articles/127760.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OSWorkflow初探(转载)</title><link>http://www.blogjava.net/livery/articles/127759.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Tue, 03 Jul 2007 02:32:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/127759.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/127759.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/127759.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/127759.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/127759.html</trackback:ping><description><![CDATA[&nbsp;&nbsp; <br><font size=3><strong>OSWorkflow 概念</strong></font>
<p>&nbsp;&nbsp;&nbsp; 在商用和开源世界里，OSWorkflow 都不同于这些已有的工作流系统。最大不同在于 OSWorkflow 有着非常优秀的灵活性。在开始接触 OSWorkflow 时可能较难掌握（有人说不适合工作流新手入门），比如，OSWorkflow 不要求图形化工具来开发工作流，而推荐手工编写 xml 格式的工作流程描述符。它能为应用程序开发者提供集成，也能与现有的代码和数据库进行集成。这一切似乎给正在寻找快速&#8220;即插即用&#8221;工作流解决方案的人制造了麻烦，但研究发现，那些&#8220;即插即用&#8221;方案也不能在一个成熟的应用程序中提供足够的灵活性来实现所有需求。<br>&nbsp;&nbsp;&nbsp; <br><font size=3><strong>OSWorkflow 优势</strong></font></p>
<p>&nbsp;&nbsp;&nbsp; OSWorkflow 给你绝对的灵活性。OSWorkflow 被认为是一种&#8220;低级别&#8221;工作流实现。与其他工作流系统能用图标表现&#8220;loops(回路)&#8221;和&#8220;conditions(条件)&#8221;相比，OSWorkflow 只是手工&#8220;编码(coded)&#8221;来实现的。但这并不能说实际的代码是需要完全手工编码的，脚本语言能胜任这种情形。OSWorkflow 不希望一个非技术用户修改工作流程，虽然一些其他工作流系统提供了简单的 GUI 用于工作流编辑，但像这样改变工作流，通常会破坏这些应用。所以，进行工作流调整的最佳人选是开发人员，他们知道该怎么改变。不过，在最新的版本中，OSWorkflow 也提供了 GUI 设计器来协助工作流的编辑。</p>
<p>&nbsp;&nbsp;&nbsp; OSWorkflow 基于有限状态机概念。每个 state 由 step ID 和 status 联合表现（可简单理解为 step 及其 status 表示有限状态机的 state）。一个 state 到另一 state 的 transition 依赖于 action 的发生，在工作流生命期内有至少一个或多个活动的 state。这些简单概念展现了 OSWorkflow 引擎的核心思想，并允许一个简单 XML 文件解释工作流业务流程。</p>
<p>&nbsp;Osworkflow是完全用java语言编写的开放源代码的工作流引擎，具有显著的灵活性及完全面向有技术背景的用户的特点。用户可以根据自身的需求利用这款开源软件设计简单或是复杂的工作流。通过使用，用户就可以把工作中心放在业务和规则的定义上，而不需通过硬编码的方式实现一个Petri网或是一个有穷自动机。用户可以以最小的代价把osworkflow整合到自己的程序中来。Osworkflow几乎提供了所有用户可能在实际流程定义中需要用到的工作流构成元素，如：环节（step）、条件（conditions）、循环（loops）、分支（spilts）、合并（joins）、角色（roles）等等。<br>&nbsp;&nbsp;&nbsp;&nbsp; 首先，在osworkflow中需要了解得最重要的概念是环节，每个工作流包含了多个环节，可&nbsp; 以把环节想象成工作流中每一个重要的活动。每个环节可以有一些诸如&#8220;已完成&#8221;、&#8220;正在处理&#8221;、&#8220;已添加至处理队列&#8221;、&#8220;未处理&#8221;等的状态，设计工作流的人可以根据需要自己定义状态。<br>&nbsp;&nbsp;&nbsp; 在每个环节，动作被用户指定为自动或手动地执行。每个动作执行后，都有一个结果（result）。结果决定了工作流的流转方向：可以停留在同一环节，跳转到另一环节，跳转到一个分支，或者汇集到一个合并等。<br>最后两个概念涉及用户对业务流程的并发执行，分支把工作流分解为两个并行的环节，合并则在用户满足一定条件后，把两个并行的环节合并成一个。<br>动作的执行代表了业务流程的执行，每个动作都有一组预处理功能（pre-functions）和一组后处理功能（post-functions）。其作用正如读者想象的那样，一个在动作触发之前执行，一个在动作触发之后执行。一个简单的例子是：可以在预处理功能中检验申请表格数据的正确性，而后在后处理贡功能中把经检验的数据保存至数据库。<br>&nbsp;&nbsp;&nbsp; 动作的执行结果可以是有条件的（conditional）或无条件的（unconditional）。对于有条件的结果，引擎将首先检查是否条件被满足，然后再交给工作流来处理。如果条件不满足的话，引擎将进一步判断下一个有条件结果是否得到满足，以此类推，直到系统最终执行到无条件结果进行处理。<br>&nbsp;&nbsp;&nbsp; 如果所有的条件结果都没有得到满足会如何呢？事实上，每个动作都强制要求具有唯一一个无条件结果。与此对应的，可以有多个有条件的结果。<br>&nbsp;&nbsp;&nbsp; 业务规则常常在最终结果中带有条件判断，比如，&#8220;如果申请来自于一个老客户，则流转到环节1&#8221;或者&#8220;如果当前系统的用户的角色是经理的话，直接流转道最后一个环节&#8221;。<br>最后一个重要的概念是步骤状态（process state），在osworkflow中，当前步骤状态是所有当前环节状态的集合。读者可能会认为工作流在运行过程中只能有一个状态，但现实的情况是：因为对分支和合并的支持，引擎能够做到对环节的并发控制，因此工作流的当前状态就可能出现：&#8220;等待风险分析及已核查财务历史&#8221;的情况。<br>&nbsp;&nbsp;&nbsp; 激活动作的用户被顺理成章地称为触发者（caller），每个环节都有一个所有者（owner），以代表在当前环节中负责执行动作的角色或用户。<br>当用户在环节中运转流程的时候，已完成的环节被保存至历史表中（history），用户当前所处的环节成为当前环节（current steps）。<br>&nbsp;&nbsp;&nbsp; 最后，在osworkflow中并不存在其他工作流引擎中所包含的工作项（workitem）的概念。这是因为osworkflow是&#8220;十分底层&#8221;的工作流实现，怎样实现或定义工作项完全交由用户来决定。笔者认为工作项的概念太过抽象，用业务数据来称呼它或许更为贴切一些。<br>&nbsp;&nbsp;&nbsp; Osworkflow 的文档中介绍了更多的构造元素，如寄存器（Registers），共用方法（common functions）等，但建议在建立好第一个工作流以后再去研究它们。它们是osworkflow基本元素外的高级特性，而我们前面所认识的元素则是osworkflow的根本所在。 </p>
<p>&nbsp;</p>
<p><font size=3><strong>OSWorkflow 核心概念</strong> </font><br>&nbsp;&nbsp;&nbsp; <br><strong>step（步骤）<br></strong>&nbsp;&nbsp;&nbsp; 一个 step 是工作流所处的位置。可能从一个 step 流转到另外一个 step（或者有时候还是停留在一样的 step）。举例来说，一个 OA 系统的请假流程，它的 step 名称可能有&#8220;本部门审批阶段&#8221;，&#8220;办公室审批阶段&#8221;，&#8220;总经理审批阶段&#8221;等。<br>&nbsp;<br><strong>status（状态）<br></strong>&nbsp;&nbsp;&nbsp; 工作流 status 是一个用来描述工作流程中具体步骤状态的字符串。OSWorkflow 的有 Underway（进行中）、Queued（等候处理中）、Finished（完成）三种 status。<br>&nbsp;<br><strong>action（动作）<br></strong>&nbsp;&nbsp;&nbsp; action 指定了可能发生在 step 内的转变，会导致 step 的变更。在 OA 系统中，&#8220;本部门审批阶段&#8221;可能有&#8220;拒绝&#8221;或&#8220;批准&#8221;两个 action。action 和 step 之间的关系是，step 说明&#8220;在哪里&#8221;，action 说明&#8220;可以去哪里&#8221;。 一个 action 典型地由两部分组成：可以执行此动作的 condition（条件），以及执行此动作的 result（结果）。<br>&nbsp;<br><strong>condition（条件）</strong><br>&nbsp;&nbsp;&nbsp; 类似于逻辑判断，可包含&#8220;AND&#8221;和&#8220;OR&#8221;逻辑。比如一个请假流程中的&#8220;本部门审批阶段&#8221;，该阶段利用&#8220;AND&#8221;逻辑，判断流程状态是否为等候处理中，以及审批者是否为本部门主管。<br>&nbsp;&nbsp;&nbsp; <br><strong>result（结果）</strong><br>&nbsp;&nbsp;&nbsp; Result 代表指向新的 step 及其 step status，也可能进入 split 或者 join。Result 分为两种， contidional-result （有条件结果），只有条件为真时才使用该结果，和 unconditional-result（无条件结果），当条件不满足或没有条件时使用该结果。</p>
<p><strong>split/join（分离/连接）</strong> <br>流程的切分和融合。很简单的概念，split 提供多个 result；join 则判断多个 current step 的状态，提供一个 result。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><font size=3><strong>OSWorkflow 包用途分析及代码片断</strong> </font><br>&nbsp;&nbsp;&nbsp; <br><strong>com.opensymphony.workflow</strong><br>&nbsp;&nbsp;&nbsp; 该包为整个 OSWorkflow 引擎提供核心接口。例如 com.opensymphony.workflow.Workflow 接口，可以说，实际开发中的大部分工作都是围绕该接口展开的，该接口有 BasicWorkflow、EJBWorkflow、OfbizWorkflow 三个实现类。</p>
<p><strong>com.opensymphony.workflow.basic <br></strong>&nbsp;&nbsp;&nbsp; 该包有两个类，BasicWorkflow 与 BasicWorkflowContext。BasicWorkflow 不支持事务，尽管依赖持久实现，事务也不能包裹它。BasicWorkflowContext 在实际开发中很少使用。</p>
<p>&nbsp;</p>
<table style="WIDTH: 417px; HEIGHT: 54px" cellSpacing=1 cellPadding=1 width=417 border=1>
    <tbody>
        <tr>
            <td><font size=2>&nbsp;&nbsp;public void setWorkflow(int userId) {<br>&nbsp;&nbsp;Workflow workflow = new BasicWorkflow(Integer.toString(userId));<br>&nbsp;}</font> </td>
        </tr>
    </tbody>
</table>
<p><strong>com.opensymphony.workflow.config</strong> <br>&nbsp;&nbsp;&nbsp; 该包有一个接口和两个该接口的实现类。在 OSWorkflow 2.7 以前，状态由多个地方的静态字段维护，这种方式很方便，但是有很多缺陷和约束。最主要的缺点是无法通过不同配置运行多个 OSWorkflow 实例。实现类 DefaultConfiguration 用于一般的配置文件载入。而 SpringConfiguration 则是让 Spring 容器管理配置信息。</p>
<p>&nbsp;</p>
<table style="WIDTH: 418px; HEIGHT: 23px" cellSpacing=1 cellPadding=1 width=418 border=1>
    <tbody>
        <tr>
            <td><font size=2>&nbsp;&nbsp;public void setConfiguration(SpringConfiguration configuration) {<br>&nbsp;&nbsp;SpringConfiguration configuration = configuration;<br>workflow.setConfiguration(configuration);<br>&nbsp;}</font> </td>
        </tr>
    </tbody>
</table>
<p><strong>com.opensymphony.workflow.ejb <br></strong>&nbsp;&nbsp;&nbsp; 该包有两个接口 WorkflowHome 和 WorkflowRemote。该包的若干类中，最重要的是 EJBWorkflow，该类和 BasicWorkflow 的作用一样，是 OSWorkflow 的核心，并利用 EJB 容器管理事务，也作为工作流 session bean 的包装器。</p>
<p><strong>com.opensymphony.workflow.loader</strong> <br>&nbsp;&nbsp;&nbsp; 该包有若干类，用得最多的是 XxxxDescriptor，如果在工作流引擎运行时需要了解指定的动作、步骤的状态、名字，等信息时，这些描述符会起到很大作用。</p>
<p>&nbsp;</p>
<table style="WIDTH: 418px; HEIGHT: 23px" cellSpacing=1 cellPadding=1 width=418 border=1>
    <tbody>
        <tr>
            <td><font size=2>&nbsp;&nbsp;public String findNameByStepId(int stepId,String wfName) {<br>&nbsp;&nbsp;WorkflowDescriptor wd = workflow.getWorkflowDescriptor(wfName);<br>&nbsp;&nbsp;StepDescriptor stepDes = wd.getStep(stepId);<br>&nbsp;&nbsp;return stepDes.getName();<br>&nbsp;}</font> </td>
        </tr>
    </tbody>
</table>
<p><strong>com.opensymphony.workflow.ofbiz <br></strong>&nbsp;&nbsp;&nbsp; OfbizWorkflow 和 BasicWorkflow 在很多方面非常相似，除了需要调用 ofbiz 的 TransactionUtil 来包装事务。</p>
<p><strong>com.opensymphony.workflow.query</strong> <br>&nbsp;&nbsp;&nbsp; 该包主要为查询而设计，但不是所有的工作流存储都支持查询。通常，Hibernate 和 JDBC 都支持，而内存工作流存储不支持。值得注意的是 Hibernate 存储不支持混合型查询（例如，一个查询同时包含了 history step 上下文和 current step 上下文）。执行一个查询，需要创建 WorkflowExpressionQuery 实例，接着调用 Workflow 对象的 query 方法来得到最终查询结果。</p>
<p>&nbsp;</p>
<table style="WIDTH: 419px; HEIGHT: 23px" cellSpacing=1 cellPadding=1 width=419 border=1>
    <tbody>
        <tr>
            <td>
            <p><font size=2>&nbsp;&nbsp;public List queryDepAdmin(int userId,int type) {<br>&nbsp;&nbsp;int[] arr = getSubPerson(userId,type);</font> </p>
            <p><font size=2>&nbsp;&nbsp;//构造表达式<br>&nbsp;&nbsp;Expression[] expressions = new Expression[1 + arr.length];<br>&nbsp;&nbsp;Expression expStatus = new FieldExpression(FieldExpression.STATUS,<br>&nbsp;&nbsp;&nbsp;&nbsp;FieldExpression.CURRENT_STEPS, FieldExpression.EQUALS, "Queued");<br>&nbsp;&nbsp;expressions[0] = expStatus;</font> </p>
            <p><font size=2>&nbsp;&nbsp;for (int i = 0; i &lt; arr.length; i++) {<br>&nbsp;&nbsp;&nbsp;Expression expOwner = new FieldExpression(FieldExpression.OWNER,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FieldExpression.CURRENT_STEPS, FieldExpression.EQUALS,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Integer.toString(arr[i]));<br>&nbsp;&nbsp;&nbsp;expressions[i + 1] = expOwner;<br>&nbsp;&nbsp;}</font> </p>
            <p><font size=2>&nbsp;&nbsp;//查询未完成流编号<br>&nbsp;&nbsp;List wfIdList = null;<br>&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;WorkflowExpressionQuery query = new WorkflowExpressionQuery(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new NestedExpression(expressions, NestedExpression.AND));<br>&nbsp;&nbsp;&nbsp;wfIdList = workflow.query(query);<br>&nbsp;&nbsp;} catch (Exception e) {<br>&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;}</font> </p>
            </td>
        </tr>
    </tbody>
</table>
<br><strong>com.opensymphony.workflow.soap</strong> <br>&nbsp;&nbsp;&nbsp; OSWorkflow 通过 SOAP 来支持远端调用。这种调用借助 WebMethods 实现。
<p><strong>com.opensymphony.workflow.spi <br></strong>&nbsp;&nbsp;&nbsp; 该包可以说是 OSWorkflow 与持久层打交道的途径，如当前工作流的实体，其中包括：EJB、Hibernate、JDBC、Memory、Ofbiz、OJB、Prevayler。</p>
<p>&nbsp;</p>
<table style="WIDTH: 422px; HEIGHT: 23px" cellSpacing=1 cellPadding=1 width=422 border=1>
    <tbody>
        <tr>
            <td><font size=2>&nbsp;&nbsp;HibernateWorkflowEntry hwfe = (HibernateWorkflowEntry) getHibernateTemplate()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.find("from HibernateWorkflowEntry where Id="<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ wfIdList.get(i)).get(0);</font></td>
        </tr>
    </tbody>
</table>
<p><strong>com.opensymphony.workflow.util</strong><br>该包是 OSWorkflow 的工具包，包括了对 BeanShell、BSF、EJB Local、EJB Remote、JNDI 的支持。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><font size=3><strong>小 结</strong></font></p>
<p>&nbsp;&nbsp;&nbsp; 由于本人所在公司希望在 OA 系统中引入工作流引擎，经过分析决定采用 OSWorkflow 引擎。利用 OSWorkflow，已经在系统中实现了请假条流程原型，该流程结合 OA 系统中已有的 RBAC 模型进行逐级审核。我个人认为要用 OSWorkflow 让某个流程跑起来似乎很麻烦，主要是需要扩展和自己实现的太多。<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 另外，引用一段 Quake Wang 的原话：电子政务/OA 如果要使用workflow engine的话，shark，jbpm 之类的workflow engine有点杀鸡用牛刀的味道。shark 和 jbpm 都强迫你使用它的用户模型，怎样把企业现有的用户模型（包括组织结构）映射过来是很繁琐的事情，比如常见的 OA 应用中，申请者对应的部门负责人为下一个流程的人工参与者，使用 shark 或者 jbpm 都得绕一圈，通过现有的人力资源系统，获得用户，再对应过来。这还仅仅是一个简单的需求，更不用说国内企业千奇百怪的组织结构，以及各种特殊流程，用 wfmc 或者其他所谓的 workflow 通用标准去做不怎么标准的事情。吃力不讨好。用 osworkflow 这种基于状态机的 workflow engine 反而会轻松很多，而且它也没有强迫你使用它的用户模型。另外纠正一点：osworkflow 不仅仅支持简单的 BeanShell，还支持 java class，bsf，ejb。如果做电子政务/OA 的话，觉得目前 osworkflow 是最适用的 opensource workflow engine。<br></p>
<img src ="http://www.blogjava.net/livery/aggbug/127759.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2007-07-03 10:32 <a href="http://www.blogjava.net/livery/articles/127759.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>