Jbpm4.4+hibernate3.5.4+spring3.0.4+struts2.1.8 整合例子(附完整的请假流程例子)。 
 1.          jbpm4.4 测试环境搭建 
 2.          Jbpm4.4+hibernate3.5.4+spring3.0.4+struts2.1. 整合环境搭建 
 3.          jbpm4.4 基础知识 
 4.          整合过程中常见问题的解决 
 5.          请假流程例子( s2sh+jbpm ) 
 6.          总结及参考文章 
 jbpm4.4 测试环境搭建 
 刚接触 jbpm 第一件事就是快速搭建环境,测试 jbpm 所给的例子。 Jbpm 是一个工作流引擎框架,如果没有 javaEE 开发环境, jbpm 也提供了安装脚本( ant ),一键提供安装运行环境。同时也可以将 jbpm 整合到 eclipse 或者 myeclipse 中。 
   
 快速搭建环境的步骤是: 
 1.          安装 jbpm-myeclipse 插件,这个插件随 jbpm4.4 一起发布,位于 jbpm-4.4/install/src/gpd 目录下,这个安装好后,就可以在myeclipse 中编辑流程图了(可视化流程设计) 
      在myeclipse->help->myeclipse configuration centre->software->add site->add from archive file  选择jbpm-4.4/install/src/gpd  下的jbpm-gpd-site.zip  
 安装这个插件应该注意断网,避免其到网上更新。同时注意:需要选择 
  双击每一项,确保每一项被加入到了
  双击每一项,确保每一项被加入到了 
  
  
 (说明:事实上不用选完,带source 的部件不用选择,为了省事就全部选择了) 
   
 提示:如果安装时不断网,jbpm 插件会自动到网上更新。同时会弹出一个错误窗口,安装速度异常缓慢。安装完成后,myeclipse 的references 菜单会变得面目全非。  
 2.          搭建 jbpm 运行环境。 
  
  
 3 .然后配置jpdl 支持 
  
  
 4. 确定是否配置jbpm 正确 
 在myeclipse->new->other->  
  
  
 关于myeclipse 中配置jbpm 请参考jbpm 的帮助文档,文档给的是在eclipse 下配置jbpm 。 
   
 5. 测试运行环境: 
 新建一个 java 项目,将 jbpm-4.4/examples 下的 src 目录, copy 到项目中。然后引入相关 jar 包, jbpm.jar 和 lib 下的所有包,先不考虑 jar 包选择问题。 Src 中包括了 jbpm 中的基本元素的使用。如 start , state , end , sql , script , fork , join 等。然后跟着 jbpm 的帮助文档,一点一点的学习。 
  
  
 说说以上文件的作用:第一个是 jbpm 的配置文件,在这个文件又引入其他的文件,在被引入的文件有一个文件包含了 
              <hibernate-configuration> <cfg resource="jbpm.hibernate.cfg.xml" /> 
             </hibernate-configuration> <hibernate-session-factory /> 
 用于创建 hibernate 的 sessionfactory 并交给 jbpm 的 IOC 容器管理。 
 第二个文件是 hibernate 配置文件,里面包含了 jbpm 框架需要的表的 hbm.xml 配置文件。 
   
   
   
 Jbpm4.4+hibernate3.5.4+spring3.0.4+struts2.1. 整合环境搭建 
 我的开发环境: 
 tomcat6.0.28+mysql5.1.30+ Jbpm4.4+hibernate3.5.4+spring3.0.4+struts2.1.8+myeclipse8.6+java jdk 6.0.23 
 在搭建环境之前,先认识一下 jbpm 。    JBPM 在管理流程时,是需要数据库表的支持的,因为底层的逻辑有那么复杂。默认下载下来的配置,使用的是( hsqldb  )内存数据库。实际应用中,我们就需要连接到我们的数据库里来。所以要事先建好相关的表,相应的 sql 文件在 /jbpm-4.4/install/src/db 下,当然,你也可以使用 hibernate 的 hibernate.hbm2ddl.auto 自动建表,本人建议自己用建表语句,会少很多麻烦(本人在此处可没少碰麻烦)。 如果不结合其他的框架进行整个开发( 如:spring 、hibernate),JBPM4 也有自己的一套IOC 容器, 能后将自己的服务配置到IOC 容器中, 能够很容易的运行容器所配置的服务, 这样它也能够在代码中减少一陀一陀的工厂类等代码的调用, 降低了偶核性, 但是如果结合spring 框架来进行整个开发的话, 那么就有两个容器, 两个SessionFactory, 但是系统中只考虑一个容器来。对服务进行管理,  那么我们就要将jbpm4 的服务移植到spring 的IOC 容器中, 让spring 来进行统一管理, 这样通过spring 的容器来管理服务、事务。 
   
 整合目标:将jbpm4 的IOC 移植到Spring 中,让spring 管理一个sessionfactory ,同时需要明确一点的是:jbpm4 对外提供服务是 ProcessEngine 。如: 
       private   RepositoryService  repositoryService ; 
       private   ExecutionService  executionService ; 
       private   HistoryService  historyService ; 
       private   TaskService  taskService ; 
       private   IdentityService  identityService ; 
 上面这些服务就是通过 ProcessEngine 获得的。 
 Spring 配置文件: 
 <!--jbpm4.4 工作流   --> 
       < bean  id = "springHelper"   class = "org.jbpm.pvm.internal.processengine.SpringHelper"  >  
          < property  name = "jbpmCfg"  value = "jbpm.cfg.xml"   />  
       </ bean > 
   
       < bean  id = "sessionFactory"   
         class = "org.springframework.orm.hibernate3.LocalSessionFactoryBean"  >  
          <!-- 
             <property name="configLocation"> 
             <value>classpath:jbpm.hibernate.cfg.xml</value> </property> 
         --> 
          < property  name = "dataSource"  ref = "dataSource"   />  
          < property  name = "hibernateProperties"  >  
              < props > 
                 < prop  key = "hibernate.dialect"  > org.hibernate.dialect.MySQLInnoDBDialect </ prop >  
                 < prop  key = "hibernate.show_sql"  > true </ prop >  
                 < prop  key = "hibernate.connection.pool_size"  > 1 </ prop >  
                 < prop  key = "hibernate.format_sql"  > true </ prop >  
                 < prop  key = "hibernate.hbm2ddl.auto"  > update </ prop >  
                 <!-- 
                <prop key="hibernate.current_session_context_class">thread</prop> 
                --> 
                 </ props > 
          </ property > 
   
          < property  name = "mappingLocations"  >  
              < list > 
                 < value > classpath:jbpm.execution.hbm.xml </ value > 
                 < value > classpath:jbpm.history.hbm.xml </ value > 
                 < value > classpath:jbpm.identity.hbm.xml </ value > 
                   < value > classpath:jbpm.repository.hbm.xml </ value > 
                 < value > classpath:jbpm.task.hbm.xml </ value > 
              </ list > 
          </ property > 
       </ bean > 
       < bean 
         class = "org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"  >  
          < property  name = "locations"  value = "classpath:jdbc.properties"  ></ property >  
       </ bean > 
   
       < bean  id = "dataSource"   
         class = "org.springframework.jdbc.datasource.DriverManagerDataSource"  >  
          < property  name = "driverClassName"  value = "${jdbc.driverClassName}"   />  
          < property  name = "url"  value = "${jdbc.url}"   />  
          < property  name = "username"  value = "${jdbc.username}"   />  
          < property  name = "password"  value = "${jdbc.password}"   />  
       </ bean > 
 Jar 包选择:(没有选择,所以会有很多无用的) 
  
  
  
  
  
  
   
 基础知识: 
 在 jbpm4.4 目录 install/src/db/create 下有: 
  
  
 这些 sql 脚本所创建的表是 jbpm 能正常工作所必须的。我们可以直接运行这些 sql 在数据库建立起相关的表(共 18 张,如下): 
  
  
 每张表对应的含义: 
 ( 1 ) JBPM4_DEPLOYMENT 
 ( 2 ) JBPM4_DEPLOYPROP 
 ( 3 ) JBPM4_LOB :存储  上传一个包含 png 和 jpdl.xml 的 zip 包 的相关数据  
 jbpm4_deployment 表多了一条记录  
 jbpm4_deployprop 表多了四条记录 , 对应 langid,pdid,pdkey,pdversion 
 jbpm4_lob  表多了二条记录 , 保存流程图 png 图片和 jpdl.xml 
 ( 4 ) JBPM4_HIST_PROCINST 与 
 ( 5 ) JBPM4_HIST_ACTINST 分别存放的是 Process Instance 、 Activity Instance 的历史记 
 ( 6 ) JBPM4_EXECUTION 主要是存放 JBPM4 的执行信息, Execution 机制代替了 JBPM3 的 Token 机制(详细参阅 JBPM4 的 PVM 机制)。  
 ( 7 ) JBPM4_TASK 存放需要人来完成的 Activities ,需要人来参与完成的 Activity 被称为 Task 。  
 ( 8 ) JBPM4_PARTICIPATION 存放 Participation 的信息, Participation 的种类有 Candidate 、 Client 、 Owner 、 Replaced Assignee 和 Viewer 。而具体的 Participation 既可以是单一用户,也可以是用户组。  
 ( 9 ) JBPM4_SWIMLANE 。 Swim Lane 是一种 Runtime Process Role 。通过 Swim Lane ,多个 Task 可以一次分配到同一 Actor 身上。  
 ( 10 ) JBPM4_VARIABLE  存的是进行时的临时变量。  
 ( 11 ) JBPM4_HIST_DETAIL 保存 Variable 的变更记录。  
 ( 12 ) JBPM4_HIST_VAR 保存历史的变量。  
 ( 13 ) JBPM4_HIST_TASKTask 的历史信息。  
 ( 14 ) JBPM4_ID_GROUP 
 ( 15 ) JBPM_ID_MEMBERSHIP 
 ( 16 ) JBPM4_ID_USER  这三张表很常见了,基本的权限控制,关于用户认证方面建议还是自己开发一套, JBPM4 的功能太简单了,使用中有很多需要难以满足。  
 ( 17 ) JBPM4_JOB  存放的是 Timer 的定义。  
 ( 18 ) JBPM4_PROPERTY 
 你可以直接运行脚本,整合中有hibernate ,所以就用hibernate 自动创建。事实上jbpm 也是采用的hibernate 作为其持久化工具。 
   
 jbpm 4.4 中一些概念( 转自family168)   
 1, 流程定义(ProcessDefinition): 对整个流程步骤的描述., 相当于我们在编程过程过程用到的类, 是个抽象的概念.  
 2. 流程实例(ProcessInstance) 代表着流程定义的特殊执行例子, 相当于我们常见的对象. 他是类的特殊化.  
 最典型的属性就是跟踪当前节点的指针.  
 3. 流程引擎(ProcessEngine),  服务接口可以从 ProcessEngine 中获得, 它是从 Configuration 构建的, 如下:  
 ProcessEngine processEngine = new Configuration()
       .buildProcessEngine(); 
 从流程引擎中可以获得如下的服务:  
 RepositoryService repositoryService = processEngine.getRepositoryService();
 ExecutionService executionService = processEngine.getExecutionService();
 TaskService taskService = processEngine.getTaskService();
 HistoryService historyService = processEngine.getHistoryService();
 ManagementService managementService = processEngine.getManagementService(); 
 4. 部署流程(Deploying a process):  
 RepositoryService 包含了用来管理发布资源的所有方法,  
 如下可以发布流程定义.  
 String deploymentid = repositoryService.createDeployment() 
      .addResourceFromClasspath("*.jpdl.xml") 
      .deploy(); 
 这个id 的格式是(key)-{version}.  
 5. 删除流程定义:  
 repositoryService.deleteDeployment(deploymentId); 可以用级联的方式, 也可以remove  
   
 6. 启动一个新的流程实例:  
   
    
   
    
   
 ProcessInstance processInstance = executionService.startProcessInstanceByKey("key"); 
   
 如果启动指定版本的流程定义 , 用下面的方法 : 
   
 ProcessInstance processInstance =executionService.startProcessInstanceById("ID"); 
   
 7. 使用变量 
 当一个新的流程实例启动时就会提供一组对象参数。 将这些参数放在variables 变量里, 然后可以在流程实例创建和启动时使用。 
 Map<String,Object> variables = new HashMap<String,Object>(); 
 variables.put("customer", "John Doe"); 
 variables.put("type", "Accident"); 
 variables.put("amount", new Float(763.74)); 
   
 ProcessInstance processInstance = 
      executionService.startProcessInstanceByKey("ICL", variables); 
 8. 执行等待的流向:  
 当使用一个 state 活动时,执行(或流程实例) 会在到达state 的时候进行等待, 
 直到一个signal (也叫外部触发器)出现。  signalExecution 方法可以被用作这种情况。 
 执行通过一个执行id (字符串)来引用。 
 executionService.signalExecutionById(executionId); 
 9.TaskService 任务服务:  
 TaskService 的主要目的是提供对任务列表的访问途径。 例子代码会展示出如何为id 为 johndoe 的 
 用户获得任务列表:  
 List<Task> taskList = taskService.findPersonalTasks("johndoe"); 
   
   
 JBPM4 –ProcessEngine 
 在jBPM 内部通过各种服务相互作用。 服务接口可以从ProcessEngine    中获得, 它是从Configuration    构建的。 
 获得ProcessEngine :     processEngine =Configuration.getProcessEngine (); 
   
 JBPM4 – RepositoryService 
 RepositoryService   包含了用来管理发布资源的所有方法。     
 部署流程     
 String deploymentid = repositoryService.createDeployment()  
     .addResourceFromClasspath("org/jbpm/examples/services/Order.jpdl.xml")  
     .deploy();   
 ZipInputStream zis =  new   ZipInputStream( this  .getClass() 
                .getResourceAsStream( "/com/jbpm/source/leave.zip" )); 
 //  发起流程,仅仅就是预定义任务,即在系统中创建一个流程,这是全局的,与具体的登陆   用户无关。然后,在启动流程时,才与登陆用户关联起来 
 String did  =  repositoryService .createDeployment() 
                .addResourcesFromZipInputStream(zis).deploy(); 
  通过上面的 addResourceFromClass  方法,流程定义 XML 的内容可以从文件,网址,字符串,输入流或 zip 输入流中获得。 
 每次部署都包含了一系列资源。每个资源的内容都是一个字节数组。 jPDL 流程文件都是以 .jpdl.xml  作为扩展名的。其他资源是任务表单和 java 类。 
 部署时要用到一系列资源,默认会获得多种流程定义和其他的归档类型。 jPDL 发布器会自动识别后缀名是 .jpdl.xml  的流程文件。 
 在部署过程中,会把一个 id  分配给流程定义。这个 id  的格式为 {key}-{version} , key 和 version 之间使用连字符连接。 
 如果没有提供 key (指在流程定义文件中,对流程的定义),会在名字的基础自动生成。生成的 key 会把所有不是字母和数字的字符替换成下划线。 
 同一个名称只能关联到一个 key ,反之亦然。 
 如果没有为流程文件提供版本号, jBPM 会自动为它分配一个版本号。请特别注意那些已经部署了的名字相同的流程文件的版本号。它会比已经部署的同一个 key 的流程定义里最大的版本号还大。没有部署相同 key 的流程定义的版本号会分配为 1 。   
 删除流程定义  
 删除一个流程定义会把它从数据库中删除。 
 repositoryService.deleteDeployment(deploymentId);   
 如果在发布中的流程定义还存在活动的流程实例,这个方法就会抛出异常。 
 如果希望级联删除一个发布中流程定义的所有流程实例,可以使用 deleteDeploymentCascade 。 
 JBPM4 – TaskService 
 TaskService 的主要目的是提供对任务列表的访问途径。   例子代码会展示出如何为 id 为 johndoe  的用户获得任务列表 
 List<Task> taskList = taskService.findPersonalTasks("johndoe");   
 一般来说,任务会对应一个表单,然后显示在一些用户接口中。   表单需要可以读写与任务相关的数据。 
 // read task variables  
 Set<String> variableNames = taskService.getVariableNames(taskId);  
 variables = taskService.getVariables(taskId, variableNames);   
 // write task variables  
 variables = new HashMap<String, Object>();  
 variables.put("category", "small");  
 variables.put("lires", 923874893);  
 taskService.setVariables(taskId, variables);   
 
 taskSerice 也用来完成任务。  
 taskService.completeTask(taskId);  
 taskService.completeTask(taskId, variables);  
 taskService.completeTask(taskId, outcome);  
 taskService.completeTask(taskId, outcome, variables);   
 这些 API 允许提供一个变量 map ,它在任务完成之前作为流程变量添加到流程里。   它也可能提供一个 “ 外出 outcome” ,这会用来决定哪个外出转移会被选中。   逻辑如下所示: 
 如果一个任务拥有一个没用名称的外向转移:   
   taskService.getOutcomes()  返回包含一个 null 值集合,。  
 taskService.completeTask(taskId)  会使用这个外向转移。  
 taskService.completeTask(taskId, null)  会使用这个外向转移。  
 taskService.completeTask(taskId, "anyvalue")  会抛出一个异常。 
 如果一个任务拥有一个有名字的外向转移:   
   taskService.getOutcomes()  返回包含这个转移名称的集合。  
 taskService.completeTask(taskId)  会使用这个单独的外向转移。  
 taskService.completeTask(taskId, null)  会抛出一个异常(因为这里没有无名称的转移)。  
 taskService.completeTask(taskId, "anyvalue")  会抛出一个异常。  
 taskService.completeTask(taskId, "myName")  会根据给定的名称使用转移。 
 如果一个任务拥有多个外向转移,其中一个转移没有名称,其他转移都有名称:   
   taskService.getOutcomes()  返回包含一个 null 值和其他转移名称的集合。  
 taskService.completeTask(taskId)  会使用没有名字的转移。  
 taskService.completeTask(taskId, null)  会使用没有名字的转移。  
 taskService.completeTask(taskId, "anyvalue")  会抛出异常。  
 taskService.completeTask(taskId, "myName")  会使用名字为 'myName' 的转移。 
 如果一个任务拥有多个外向转移,每个转移都拥有唯一的名字:   
   taskService.getOutcomes()  返回包含所有转移名称的集合。  
 taskService.completeTask(taskId)  会抛出异常,因为这里没有无名称的转移。  
 taskService.completeTask(taskId, null)  会抛出异常,因为这里没有无名称的转移。  
 taskService.completeTask(taskId, "anyvalue")  会抛出异常。  
 taskService.completeTask(taskId, "myName")  会使用名字为 'myName' 的转移。 
 任务可以拥有一批候选人。候选人可以是用户也可以是用户组。用户可以接收自己是候选人的任务。接收任务的意思是用户会被设置为被分配给任务的人。在那之后,其他用户就不能接收这个任务了。 
 人们不应该在任务做工作,除非他们被分配到这个任务上。用户界面应该显示表单,如果他们被分配到这个任务上,就允许用户完成任务。对于有了候选人,但是还没有分配的任务,唯一应该暴露的操作就是 “ 接收任务 ” 。 
   
 JBPM4 – ExecutionService 
 最新的流程实例   -- ByKey   
  下面是为流程定义启动一个新的流程实例的最简单也是   最常用的方法: 
 ProcessInstance processInstance = executionService.startProcessInstanceByKey ("ICL"); 
 上面 service 的方法会去查找  key 为 ICL 的最新版本的流程定义,   然后在最新的流程定义里启动流程实例。 
 当 key 为 ICL 的流程部署了一个新版本,  startProcessInstanceByKey 方法会自动切换到最新部署的版本。 
 原来已经启动的流程,还是按照启动时刻的版本执行。 
 
  指定流程版本   -- ById 
   换句话说,你如果想根据特定的版本启动流程实例,   便可以使用流程定义的 id 启动流程实例。如下所示: 
 ProcessInstance processInstance = executionService.startProcessInstanceById ("ICL-1"); 
 
  使用  key   
 我们可以为新启动的流程实例分配一个 key( 注意:   这个 key 不是 process 的 key ,而是启动的 instance 的 key ) ,这个 key 是用户执行的时候定义的,有时它会作为 “ 业务 key” 引用。一个业务 key 必须在流程定义的所有版本范围内是唯一的。通常很容易在业务流程领域找到这种 key 。比如,一个订单 id 或者一个保险单号。  
 ProcessInstance processInstance = executionService.startProcessInstanceByKey ("ICL", "CL92837");  
 // 2 个参数:  
 //   第一个参数 processkey ,通过这个 key 启动 process 的一个实例  
 //   第二个参数为这里所说的实例 key(instance key) 
 key 可以用来创建流程实例的 id ,格式为 {process-key}.{execution-id} 。所以上面的代码会创建一个 id 为  ICL.CL92837 的流向( execution )。 
 如果没有提供用户定义的 key ,数据库就会把主键作为 key 。   这样可以使用如下方式获得 id :  
 ProcessInstance processInstance = executionService.startProcessInstanceByKey ("ICL"); 
 String pid = processInstance.getId(); 
 最好使用一个用户定义的   key   。  特别在你的应用代码中,找到这样的 key 并不困难。提供给一个用户定义的 key ,你可以组合流向的 id ,而不是执行一个基于流程变量的搜索  -  那种方式太消耗资源了。   
 使用变量     
 当一个新的流程实例启动时就会提供一组对象参数。   将这些参数放在 variables 变量里,   然后可以在流程实例创建和启动时使用。  
 Map<String,Object> variables = new HashMap<String,Object>(); 
 variables.put("customer", "John Doe"); 
 variables.put("type", "Accident"); 
 variables.put("amount", new Float(763.74));  
 ProcessInstance processInstance = executionService.startProcessInstanceByKey ("ICL", variables);  
 启动  instance    
 启动 instance ,必须要知道 processdefinition 的信息: processdefinition 可以通过 2 种方式获取:  
 ByKey :通过 ProcessKey ,启动该 Process 的最新版本  
 ById :   通过 Process 的 ID ,启动该 Process 的特定的版本 
 其他的参数,其余还可以在启动 Instance 的时候,给流程 2 个参数:  
 InstanceKey :这个 instanceKey 必须在整个流程定义的所有范围版本中唯一,如果用户不给于提供,系统也会自己生成;  
  一个 Map<String, ?> 表:启动流程时候给予的变量信息   
 执行等待的流向     
 当使用一个 state  活动时,执行(或流程实例)会在到达 state 的时候进行等待,直到一个 signal  (也叫外部触发器)出现。  signalExecution 方法可以被用作这种情况。执行通过一个执行 id  (字符串)来引用。 
 在一些情况下,到达 state 的执行会是流程实例本身。但是这不是一直会出现的情况。在定时器和同步的情况,流程是执行树形的根节点。所以我们必须确认你的 signal 作用在正确的流程路径上。 
 获得正确的执行的比较好的方法是给 state 活动分配一个事件监听器,像这样:  
 <state name="wait">  
   <on event="start">  
     <event-listener class="org.jbpm.examples.StartExternalWork" />  
   </on>  
   ...  
 </state>   
 在事件监听器 StartExternalWork  中,你可以执行那些需要额外完成的部分。在这个事件监听器里,你也可以通过 execution.getId()  获得确切的流程 id 。那个流程 id ,在额外的工作完成后,你会需要它来提供给 signal 操作的: 
 executionService.signalExecutionById (executionId);   
 这里有一个可选的(不是太推荐的)方式,来获得流程 id ,当流程到达 state  活动的时候。只可能通过这种方式获得执行 id ,如果你知道哪个 JBPM API 调用了之后,流程会进入 state  活动: 
 // assume that we know that after the next call  
 // the process instance will arrive in state external work  
 ProcessInstance processInstance = executionService.startProcessInstanceById(processDefinitionId);   
 // or ProcessInstance processInstance =  
 //  executionService.signalProcessInstanceById(executionId);  
 Execution execution = processInstance.findActiveExecutionIn("external work");  
 String executionId = execution.getId();   
   
 JBPM4 – HistoryService 
 在流程实例执行的过程中,会不断触发事件。从那些事件中,运行和完成流程的历史信息会被收集到历史表中。 
 HistoryService  提供了   对那些信息的访问功能。 
 如果想查找某一特定流程定义的所有流程实例,   可以像这样操作: 
 List<HistoryProcessInstance> historyProcessInstances = historyService  
   .createHistoryProcessInstanceQuery()  
   .processDefinitionId("ICL-1")  
   .orderAsc(HistoryProcessInstanceQuery.PROPERTY_STARTTIME)  
   .list();   
 单独的活动流程也可以作为 HistoryActivityInstance  保存到历史信息中。 
 List<HistoryActivityInstance> histActInsts = historyService  
     .createHistoryActivityInstanceQuery()  
     .processDefinitionId("ICL-1")  
     .activityName("a")  
     .list();   
 也可以使用简易方法 avgDurationPerActivity 和 choiceDistribution 。可以通过 javadocs 获得这些方法的更多信息。 
 有时,我们需要获得指定流程实例已经过的节点的完整列表。下面的查询语句可以用来获得所有已经执行的节点列表: 
 List<HistoryActivityInstance> histActInsts = historyService  
     .createHistoryActivityInstanceQuery()  
     .processInstanceId("ICL.12345")  
     .list();   
 上面的查询与通过 execution id 查询有一些不同。有时 execution id 和流程实例 id 是不同的,   当一个节点中使用了定时器, execution id 中就会使用额外的后缀,   这就会导致当我们通过 execution id 查询时,   这个节点不会出现在结果列表中。 
   
 整合过程中常见问题的解决 
 错误 1 : java.lang.LinkageError: loader constraint violation: when resolving interface method "javax.servlet.jsp.JspApplicationContext.getExpressionFactory()Ljavax/el/ExpressionFactory;" the class loader (instance of org/apache/jasper/servlet/JasperLoader) of the current class, org/apache/jsp/index_jsp, and the class loader (instance of org/apache/catalina/loader/StandardClassLoader) for resolved class, javax/servlet/jsp/JspApplicationContext, have different Class objects for the type javax/el/ExpressionFactory used in the signature 
 错误的解决办法。( Tomcat6.0.28 ) 
 exception  
 javax.servlet.ServletException: java.lang.LinkageError: loader constraint violation: when resolving interface method "javax.servlet.jsp.JspApplicationContext.getExpressionFactory()Ljavax/el/ExpressionFactory;" the class loader (instance of org/apache/jasper/servlet/JasperLoader) of the current class, org/apache/jsp/OnDuty/wfmanage_jsp, and the class loader (instance of org/apache/catalina/loader/StandardClassLoader) for resolved class, javax/servlet/jsp/JspApplicationContext, have different Class objects for the type javax/el/ExpressionFactory used in the signature org.apache.jasper.servlet.JspServlet.service(JspServlet.java:275) javax.servlet.http.HttpServlet.service(HttpServlet.java:717)   
   
 root cause  
 java.lang.LinkageError: loader constraint violation: when resolving interface method "javax.servlet.jsp.JspApplicationContext.getExpressionFactory()Ljavax/el/ExpressionFactory;" the class loader (instance of org/apache/jasper/servlet/JasperLoader) of the current class, org/apache/jsp/OnDuty/wfmanage_jsp, and the class loader (instance of org/apache/catalina/loader/StandardClassLoader) for resolved class, javax/servlet/jsp/JspApplicationContext, have different Class objects for the type javax/el/ExpressionFactory used in the signature org.apache.jsp.OnDuty.wfmanage_jsp._jspInit(wfmanage_jsp.java:27) org.apache.jasper.runtime.HttpJspBase.init(HttpJspBase.java:52) org.apache.jasper.servlet.JspServletWrapper.getServlet(JspServletWrapper.java:159) org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:329) org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:342) org.apache.jasper.servlet.JspServlet.service(JspServlet.java:267) javax.servlet.http.HttpServlet.service(HttpServlet.java:717) 
   
 原因是项目中WEB-INF/lib 中的三个jar 包 (juel.jar, juel-engine.jar, juel-impl.jar )  和tomcat6 下lib 中jar 包(  el-api.jar ) 冲突 
 解决方法:   
 方法一:换成tomcat5.5  一点问题也没有了(有新版本了还用老版本?) 
   
 方法二:将 juel.jar, juel-engine.jar, juel-impl.jar 这三个包复制到tomcat6 下lib 中,并删除原来的 el-api.jar ,切记要把WEB-INF/lib 中的juel.jar, juel-engine.jar, juel-impl.jar 删除。不然还是要冲突。 
 错误 2 : org.jbpm.api.JbpmException: No unnamed transitions were found for the task '??' 
 如果一个任务拥有一个没用名称的外向转移: 
 taskService.getOutcomes()  返回包含一个 null 值集合,。 taskService.completeTask(taskId)  会使用这个外向转移。 taskService.completeTask(taskId, null)  会使用这个外向转移。 taskService.completeTask(taskId, "anyvalue")  会抛出一个异常。 
 如果一个任务拥有一个有名字的外向转移: 
 gtaskService.getOutcomes()  返回包含这个转移名称的集合。 taskService.completeTask(taskId)  会使用这个单独的外向转移。 taskService.completeTask(taskId, null)  会抛出一个异常(因为这里没有无名称的转移)。 taskService.completeTask(taskId, "anyvalue")  会抛出一个异常。 taskService.completeTask(taskId, "myName")  会根据给定的名称使用转移。 
 如果一个任务拥有多个外向转移,其中一个转移没有名称,其他转移都有名称: 
 taskService.getOutcomes()  返回包含一个 null 值和其他转移名称的集合。 taskService.completeTask(taskId)  会使用没有名字的转移。 taskService.completeTask(taskId, null)  会使用没有名字的转移。 taskService.completeTask(taskId, "anyvalue")  会抛出异常。 taskService.completeTask(taskId, "myName")  会使用名字为 'myName' 的转移。 
 如果一个任务拥有多个外向转移,每个转移都拥有唯一的名字: 
 taskService.getOutcomes()  返回包含所有转移名称的集合。 taskService.completeTask(taskId)  会抛出异常,因为这里没有无名称的转移。 taskService.completeTask(taskId, null)  会抛出异常,因为这里没有无名称的转移。 taskService.completeTask(taskId, "anyvalue")  会抛出异常。 taskService.completeTask(taskId, "myName")  会使用名字为 'myName' 的转移。 
   
 解决方案: 
   
 根据以上分析,可得到解决方案: 
   
 1 、只拥有一个外向转移时(对应上文所述 1 、 2 情况): 
   
 Map map = new HashMap(); 
 map.put("",…… ) // 各种参数 
 taskService.setVariables(taskId,map); 
 taskService.completeTask(taskId); 
   
 3 、拥有多个外向转移时(上文 3 、 4 种情况): 
 Map map = new HashMap(); 
 map.put("",…… ) // 各种参数 
 taskService.setVariables(taskId,map); 
   
 // 如想转移至有名称的外向转移: 
 taskService.completeTask(taskId," 外向转移名称 "); 
   
 // 如想转移至无名称的外向转移: 
 taskService.completeTask(taskId); 
 错误3 :*.jpdl.xml 中文乱码问题。 
 在myeclipse 的配置文件myeclipse.ini 中加入: 
 -DFile.encoding=UTF-8 
   
 请假流程例子( s2sh+jbpm ) 
  
  
   
 流程图: 
  
  
   
 <? xml  version = "1.0"  encoding = "UTF-8"  ?>  
   
 < process  name = "leave"  xmlns = "http://jbpm.org/4.4/jpdl"  >  
      < start  g = "214,37,48,48"  name = "start1"  >  
         < transition  g = "-47,-17"  name = "to    申请  "   to = "   申请  "  /> 
      </ start > 
      < task  assignee = "#{owner}"  form = "request.html"   g = "192,126,92,52"   name = "   申请  "  > 
         < transition  g = "-71,-17"  name = "to    经理审批  "   to = "   经理审批  "  /> 
      </ task > 
      < task  assignee = "manager"  form = "manager.html"   g = "194,241,92,52"   name = "   经理审批  "  > 
         < transition  g = "-29,-14"  name = "   批准  "   to = "exclusive1"  />  
         < transition  g = "105,267;103,152:-47,-17"  name = "   驳回  "   to = "   申请  "  /> 
      </ task > 
      < decision  expr = "#{day > 3 ? '   老板审批  ' : '  结束  '}"   g = "218,342,48,48"  name = "exclusive1"  >  
         < transition  g = "415,367:-47,-17"  name = "   老板审批  "   to = "   老板审批  "  /> 
         < transition  g = "-31,-16"  name = "   结束  "   to = "end1"  />  
      </ decision > 
      < end  g = "219,499,48,48"  name = "end1"  />  
      < task  assignee = "boss"  form = "boss.html"   g = "370,408,92,52"  name = "   老板审批  "  > 
         < transition  g = "415,524:-91,-18"  name = "   结束  "   to = "end1"  />  
      </ task > 
 </ process > 
 步骤: 
 发布流程:将画好的流程图,发布到jbpm 框架中(放到jbpm 数据库中),这个流程是全局的,与用户无关。发布流程后会返回一个流程id ,我们会用流程id 得到 ProcessDefinition 流程定义。 发布方法如下: 
 public    void   deploy() { 
          // repositoryService.createDeployment().addResourceFromClasspath( 
          // "/com /jbpm /source/leave.jpdl.xml").deploy(); 
         ZipInputStream zis =  new   ZipInputStream( this  .getClass() 
                .getResourceAsStream( "/com/jbpm/source/leave.zip" )); 
          //  发起流程,仅仅就是预定义任务,即在系统中创建一个流程,这是全局的,与具体的登陆   用户无关。然后,在启动流程时,才与登陆用户关联起来 
         String did  =  repositoryService .createDeployment() 
                .addResourcesFromZipInputStream(zis).deploy(); 
      } 
 启动流程:流程定义好后,并不能用,我们需要将其实例化,实例化流程将关联用户,同时将实例写入数据库中。启动流程方法如下: 
 public    void   start(String id, Map<String , Object> map) { 
          executionService .startProcessInstanceById(id, map); 
      } 
 流程一旦启动就通过start 节点,流到下一个任务节点。 
 获取待办任务列表:不同的用户登录后通过如下方式获得自己的待办任务 
       public   List<Task> getTasks(String roleName) { 
          return    taskService .findPersonalTasks(roleName); 
      } 
 在流程中每一个任务节点都关联了一个 action 请求,用于处理待办任务的视图( view ) 
 不多说了,哥就相信源码: http://download.csdn.net/source/3223403
另一例子 http://zwllxs.iteye.com/blog/726303
 
 总结及参考文章: 
 参考文章:http://www.blogjava.net/paulwong/archive/2009/09/07/294114.html   
 http://zjkilly.iteye.com/blog/738426  
 http://fish119.iteye.com/blog/779379  
   
 http://alimama.iteye.com/blog/567651  
 其他参考资料: family168 网, http://code.google.com/p/family168/downloads/list  
   
 控制流程活动: 
  
  
 原子活动: 
 