一、spring的配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=" http://www.springframework.org/schema/beans "
       xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "
       xmlns:context=" http://www.springframework.org/schema/context "
       xmlns:aop=" http://www.springframework.org/schema/aop "
       xmlns:tx=" http://www.springframework.org/schema/tx "
       xsi:schemaLocation=" http://www.springframework.org/schema/beans            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-3.0.xsd  http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  http://www.springframework.org/schema/tx            http://www.springframework.org/schema/tx/spring-tx-3.0.xsd ">


   
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">

  

<property name="driverClass" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
    <property name="user" value="root"/>
    <property name="password" value="cqsztt"/>
    <property name="initialPoolSize" value="3" />
    <property name="minPoolSize" value="3" />
    <property name="maxPoolSize" value="50" />
    <property name="maxIdleTime" value="600" />
    <property name="maxStatements" value="100" />
    <property name="acquireIncrement" value="3" /></bean>

<!--spring的配置文件有两处需要改动的地方,此为1:

     springHelper实现了ApplicationContextAware接口,springHelper可以从spring容器中获得ApplicationContext,

     然后springHelper可以利用ApplicationContext和jbpm.cfg.xml(jbpm的配置文件)一起生成伟大的processEngine。

    配置到这一步的时候,有些人就开始着急测试,多次碰壁后,有的高手居然想到了使用Spring 的IOC功能自己写个processEngin工厂

    ,大家都知道jbpm使用了hibernate,但如何将hibernate的sessionFactory注入给JBPM呢?莫及,继续往下看

-->
<bean id="springHelper" class="org.jbpm.pvm.internal.processengine.SpringHelper" /><bean id="processEngine" factory-bean="springHelper" factory-method="createProcessEngine" />
<!--Hibernate SessionFatory-->
<bean id="sessionFactory"
   class="com.travelsky.bravo.core.utils.AutoLoadSessionFactoryBean">
   <property name="dataSource" ref="dataSource" /><property name="mappingResources">
        <list>

            <!--此处为第二处需要修改的地方

               此处千万不要忘记了 ,这5个映射关系会在spring容器初始化的时候persistent到数据库中,如果没有,肯定会报exception

               写到这里我要说明下:看过jbpm有个关于hibernate配置的文件:jbpm.hibernate.cfg.xml,我这种写法就是要去掉这个文件,

               将spring + jbmp都使用一个Hibernate的配置,也就不会出现两个sessionFactory和两个database的情况了。有的同学可 发表不同的意见认为:将hibernate的配置信息写到一个单独的文件里,感觉清爽些。但是,无论如何,你总得在这个文件里配置 sessionFactory吧,所以总得配置hibernate的东东,所以干脆写在一起,将来修改起来也方便,不需要打开两个文件,至于其他细节东西 我就不到啰嗦了,到此,spring的配置文件就结束了。

       

-->
            <value>jbpm.repository.hbm.xml</value>
            <value>jbpm.execution.hbm.xml</value>
            <value>jbpm.history.hbm.xml</value>
            <value>jbpm.task.hbm.xml</value>
            <value>jbpm.identity.hbm.xml</value> 
        </list>
    </property>
    <property name="hibernateProperties">
    <props>
     <prop key="hibernate.dialect">
      org.hibernate.dialect.Oracle10gDialect
     </prop>
     <prop key="hibernate.show_sql">true</prop>
     <prop key="hibernate.format_sql">true</prop>
     <prop key="hibernate.cache.provider_class">
      org.hibernate.cache.EhCacheProvider
     </prop>
     <prop key="hibernate.cache.use_query_cache">true</prop>
    </props>
   </property></bean>

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

<!--事务管理的配置,不这么配置不影响ssh+jbpm整合-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
      <tx:attributes>
          <tx:method name="save" propagation="REQUIRED"/>
          <tx:method name="update" propagation="REQUIRED"/>
          <tx:method name="is*" read-only="true"/>
      </tx:attributes>
</tx:advice>
<aop:config>
      <aop:pointcut expression="execution(public * com.cqs.service.impl.*.*(..))" id="userServicePointcut"/>
      <aop:advisor advice-ref="txAdvice" pointcut-ref="userServicePointcut"/>
</aop:config>
<!--不影响ssh+jbpm整合-->
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
      <property name="sessionFactory" ref="sessionFactory"></property>
</bean>

</beans>

二、 jbpm的配置文件

1、jbpm.cfg.xml 
< jbpm-configuration > 
< import resource =" jbpm.default.cfg.xml " /> 
< import resource =" jbpm.tx.spring.cfg.xml " /><!--jbpm.tx.hibernate.cfg.xml不用了,无需其他配置-->
< import resource =" jbpm.jpdl.cfg.xml " /> 
< import resource =" jbpm.bpmn.cfg.xml " /> 
< import resource =" jbpm.identity.cfg.xml " /> 
< import resource =" jbpm.businesscalendar.cfg.xml " /></ jbpm-configuration >

三、 部署到Tomcat 
整合struts2就没什么好讲的了,主要是spring和struts2整合,jbpm部署到tomcat6的时候,主要jar包冲突的问 题,tomcat的lib里面的东西和项目lib里面的jar包版本等不一样的时候容易造成load到内存中的字节码出现乱七八糟的问题。 
需要特别说明的是 
1、WEB-INF/lib下面的三个jar包juel.jar,juel-engine.jar,juel-impl.jar剪切 到tomcat的lib下 
2、在tomcat的lib中新加javaee.jar,重新部署,并启动tomcat,如果成功即可,不成功需要删除tomcat lib下的el-api.jar 
3、有人数tomcat5下不会出现jar包冲突的情况,我不知道,没用过,我就不试了,哈哈


对Tomcat的修改



在将jBPM与web项目结合的时候,需要把jBPM资源包中的juel-engine.jar和juel-impl.jar添加到tomcat/lib下,同时在Web应用的lib下面,把juel-api.jar、juel-engine.jar和juel-impl.jar 删除掉。



注意,以后在建立web工程的时候,需要选择修改后的tomcat。

将jPDL的图片发布到jBPM中


1:首先把jpdl和png打到一起,打成zip包。


2:然后修改流程定义的代码,通过读取zip文件的方式来发布:


ProcessEngine processEngine = Configuration.getProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
//repositoryService.createDeployment().addResourceFromClasspath("leave.jpdl.xml").deploy();
ZipInputStream zis = new ZipInputStream(this.getClass().getResourceAsStream("/doc.zip")); 
repositoryService.createDeployment().addResourcesFromZipInputStream(zis).deploy();

  将jPDL的图片发布到jBPM中,页面上的代码示例如下:

<%!
public InputStream showWebPicture(){
ProcessEngine engine = Configuration.getProcessEngine();
RepositoryService rs = engine.getRepositoryService();
ProcessDefinition pd = rs.createProcessDefinitionQuery().processDefinitionId("at-4").uniqueResult();
InputStream in = rs.getResourceAsStream(pd.getDeploymentId(), "at.png");
return in;
}
%><%
InputStream in = showWebPicture();
byte bs[] = new byte[1024];
while(in.read(bs)!=-1){
response.getOutputStream().write(bs);
}
out.clear();
out = pageContext.pushBody();
%>
ProcessInstance pi = executionService.findProcessInstanceById("test1.10001");
Set<String> activityNames = pi.findActiveActivityNames();
 
for (String activityName:activityNames){
String pdId = pi.getProcessDefinitionId();
ActivityCoordinates ac = repositoryService.getActivityCoordinates(pdId,activityName);
System.out.println("activityName="+activityName);
System.out.println("x="+ac.getX());
System.out.println("y="+ac.getY());
System.out.println("height="+ac.getHeight());
System.out.println("width="+ac.getWidth());
System.out.println("---------------------");
}
<%
ProcessInstance pi = executionService.findProcessInstanceById("test1.10001");
Set<String> activityNames = pi.findActiveActivityNames();
for (String acitiveAcitityName:activityNames){
String pdId = pi.getProcessDefinitionId();
ActivityCoordinates ac = repositoryService.getActivityCoordinates(pdId,acitiveAcitityName);
%>
<div style="position:absolute;border:1px solid red;
left:<%=ac.getX()%>px;top:<%=ac.getY()%>px;
width:<%=ac.getWidth()%>px;height:<%=ac.getHeight()%>px;">
</div>
<%}%>
ProcessInstance pi = executionService.findProcessInstanceById("test1.10001");
Set<String> activityNames = pi.findActiveActivityNames();
for (String acitiveAcitityName:activityNames){
String pdId = pi.getProcessDefinitionId();
ActivityCoordinates ac = repositoryService.getActivityCoordinates(pdId,acitiveAcitityName);
%>
<div style="position:absolute;border:1px solid red;
left:<%=ac.getX()%>px;top:<%=ac.getY()%>px;
width:<%=ac.getWidth()%>px;height:<%=ac.getHeight()%>px;">
</div>
<%}%>

 

会签概念


会签,又称会审,也就是流程中某个业务需要经过多人表决,并且根据表决意见的汇总结果,匹配设定的规则,决定流程的走向。


 


请大家回忆一下Task节点的候选人,是一个概念吗?


 


会签示例


会签只是一个概念,并不是说所有的人都必须进行响应。举几个例子:


1:去掉一个最高分、去掉一个最低分。


2:十位领导进行会签,6个同意就算同意。


3:仅仅收集大家的意见,需要所有的人响应。


4:核心领导的一票否决


……


customer节点


在jpdl中,并没有单独的会签活动,需要我们自己写代码去实现,可以使用customer节点,自己实现自定义的功能。


实现注意点


与普通的Task任务相比,流程引擎也会停在会签节点等待人为的响应。但是,会签涉及到多个人,所以,请注意:


1:当流程进入会签节点的时候,需要生成多个任务,分发给会签涉及到的多个人。


2:在某个人响应会签的时候,有可能其他人还没有响应,这时候仍然需要等待其他人的响应;也有可能有足够的人进行了响应,这时候根据大家的意见,选择进入流向下一个活动。


customer节点的实现


写一个类,实现ExternalActivityBehaviour接口,它有两个方法需要实现,分别是execute和signal,定义如下:

public void execute(ActivityExecution aexe) throws Exception 
         
          

             
         
          

           public void signal(ActivityExecution aexe, String outComingName, 
         
          

           Map<String, ?> params)throws Exception

 


execute方法


当execution到达这个活动的时候触发,也就是运行到这个活动,创建这个活动的实例的时候就触发这个方法。


signal方法


处理一个外部的signal,也就是处理一次用户的singal请求。


实现会签的execute方法


当流程进入会签节点的时候,需要生成多个任务,分发给会签涉及到的多个人。


实现的基本思路为:


1:为customer节点创建一个task,好让流程停留在customer节点


2:为主task创建多个子task,每个参与会签的人员一个task


3:把当前的HistoryTaskInstanceActivity保存下来,后面恢复用


4:让流程停留在这里,等待外部来signal


实现会签的execute方法


当流程进入会签节点的时候,需要生成多个任务,分发给会签涉及到的多个人。


public void execute(ActivityExecution aexe) throws Exception { 
             
              

               ExecutionImpl exe = (ExecutionImpl)aexe; 
             
              

               DbSession session = EnvironmentImpl.getFromCurrent(DbSession.class);


//1:为customer1创建一个task,好让流程停留在customer1


//1.1定义task对象


TaskImpl mainTask = session.createTask();
mainTask.setAssignee(null);
mainTask.setName("HQ");
mainTask.setActivityName(aexe.getActivityName());
mainTask.setExecution(exe);
mainTask.setExecutionDbid(exe.getDbid());
mainTask.setSignalling(false);

 

//把这个task与流程实例关联起来


mainTask.setProcessInstance(exe.getProcessInstance());


//1.2保存这个对象


session.save(mainTask);


//1.3把这个对象和history连接起来


HistoryEvent.fire(new TaskActivityStart(mainTask),exe);


//2:为主task创建多个子task,每个参与会签的人员一个task


String users = (String) exe.getVariable("hqrs");
List<String> hqUsers = new ArrayList<String>();
for(String user : users.split(",")){
hqUsers.add(user);
 
TaskImpl subTask = mainTask.createSubTask();
subTask.setActivityName(aexe.getActivityName());
subTask.setAssignee(user);
//子对象不应该有execution对象,它使用父task的execution对象
// subTask.setExecution(exe);
subTask.setName(mainTask.getName()+"_"+user);
subTask.setSignalling(false);
subTask.setExecutionDbid(exe.getDbid());
 
session.save(subTask);
 
HistoryEvent.fire(new TaskActivityStart(subTask),exe);
}

 

//把应该参与会签的人员保存下来,后面好用到


exe.setVariable("hqUsers", hqUsers);


//4.4相比4.3在Custom节点execution后新加了对((ExecutionImpl) execution).historyAutomatic()的调用,


//这将在HistoryActivityInstance表中加一条自动历史记录,会造成如果Custom节点产生了Task,


//则根据execution取得的HistoryTaskInstanceActivity是错误的,因此先存下来,后面好恢复


exe.setVariable("historyAi", exe.getHistoryActivityInstanceDbid());


 


//3:让流程停留在这里,等待signal


exe.waitForSignal(); 
                   
                    

                     }


实现会签的signal方法


在某个人响应会签,在外部调用了singal的时候,会签节点需要做出相应的处理,以响应外部signal。


实现的基本思路为:


1:判断这个人是否应该参与会签


2:判断这个人是否会签过了


3:如果都没有,把这个人的任务结束了


4:把这个人的会签结果保存下来


5:然后根据业务规则进行判断,看是否满足会签结束的条件


6:如果满足


6.1先结束其他还没有完成会签的任务


6.2结束会签主任务


6.3判断应该走哪一条转移


6.4获取转移,然后接收它,从而实现流转


7:如果不满足,那么就继续等待


public void signal(ActivityExecution aexe, String outComingName, Map<String, ?> params)throws Exception {
ProcessEngine engine = Configuration.getProcessEngine();
ExecutionImpl exe =(ExecutionImpl)aexe;
TaskService ts = engine.getTaskService();


List hqUsers= (List<String>) exe.getVariable("hqUsers");
String user = (String)params.get("oper");
if(!hqUsers.contains(user)){
//继续等待
System.out.println("你不能参与会签");
exe.waitForSignal();
return;
}

 

//2:判断这个人是否会签过了


//2.1先找到主task


Task mainTask = ts.createTaskQuery().processInstanceId(exe.getProcessInstance()
.getId()).activityName("custom1").uniqueResult();
//2.2找到所有的子task
List<Task> subTaskList = ts.getSubTasks(mainTask.getId());
boolean hasHQ = true;
for(Task t : subTaskList){

 

//2.3判断这个人是否会签过了


if(user.equals(t.getAssignee())){


//存在就表示还没有会签,就把这个任务终止了


hasHQ = false;
ts.completeTask(t.getId());
break;
}
}

 //2.3如果是以前会签过的,就停止

if(hasHQ){
System.out.println("你已经会签过了");
//继续等待
exe.waitForSignal();
return;
}

 

//3:如果都没有,把这个人的任务结束了,上面已经做了


//4:把这个人的会签结果保存下来


Map<String,String> results = new HashMap<String,String>();
Object tempObject = exe.getVariable("results");
if(tempObject!=null){
results = (Map<String,String>)tempObject;
}
results.put(user, ""+params.get("result"));
exe.setVariable("results", results);


int ok = 0;
int no = 0;
for(String s : results.values()){
if("yes".equals(s)){
ok++;
}else if("no".equals(s)){
no++;
}
}
if(ok > hqUsers.size()/2){
//说明ok了,转到toT2

 

//6:如果满足


//6.1先结束其他还没有完成会签的任务


//6.2结束会签主任伍


overTasks(exe);


//6.3判断应该走哪一条转移


Transition t = exe.getActivity().getOutgoingTransition("toT2");


//6.4获取转移,然后接收它,从而实现流转


exe.take(t);
}else if(no > hqUsers.size()/2){
//说明no了,转到toT3
overTasks(exe);
Transition t = exe.getActivity().getOutgoingTransition("toT3");
exe.take(t);
}else{


exe.waitForSignal();
}
}
private void overTasks(ExecutionImpl exe){
ProcessEngine engine = Configuration.getProcessEngine();
TaskService ts = engine.getTaskService();
Task mainTask = ts.createTaskQuery().processInstanceId(
exe.getProcessInstance().getId())
.activityName("custom1").uniqueResult();


List<Task> subTaskList = ts.getSubTasks(mainTask.getId());
for(Task t : subTaskList){
ts.completeTask(t.getId());
}

//2.3终止主task 
                          
                           

                            //2.1先恢复HistoryActivityInstanceDbid 
                          
                           

                            exe.setHistoryActivityInstanceDbid( (Long) exe.getVariable("historyAi")); 
                          
                           

                            ts.completeTask(mainTask.getId()); 
                          
                           

                            }


委派介绍


委派,又称代理,是一种很常见的任务再分配机制。


比如,公司所有的个人公积金业务都需要文书小王来处理;但是,如果小王出差了呢?难道要等他回来再处理么?这时候就需要把任务委派给别人。


 


委派的实现


在jPDL里也没有委派这种活动,同样需要由我们自己来实现。


一个很好的思路就是:实现AssignmentHandler接口,在assign方法的实现里面,通过检索数据库,查看每个人的onDuty字段,来判断该工作人员是否在工作状态;如果不再工作状态,那么就去委派表里面检索指定的相应的委派人员,然后将任务分配给他即可。