打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
prp中的Jbpm替代方案
  • prp中的Jbpm替代方案

    忍不住又搞了搞jbpm,上学期初的时候搞了搞流程定义和简单的Task调度,这几天想了想,过去的想法是没错的,只是不够深入,在springmodules的激励下,想了想如果prp中使用的是jbpm,而不是osworkflow,应该如何设计。
     
    首先索性使用了jbpm的designer,载了ecilpse3.2,install了jbpm3.1自带的gpd插件,按照http://docs.jboss.com/jbpm/v3/demos/movies/jbpm-overview.htm简单地学习了一下。
     
    先新建jbpm的工程,然后新建一个process definition,一步步建立了一个类似prp中需求的流程,下面是流程定义(processdefinition.xml):
     
    <?xml version="1.0" encoding="UTF-8"?>
    <process-definition
      xmlns="urn:jbpm.org:jpdl-3.1"
      name="ProjectWorkflow">
     
       <swimlane name="applier"></swimlane>
       <swimlane name="admin"></swimlane>
      
       <start-state name="Apply Project">
          <task name="request project" swimlane="applier">
             <controller>
                <variable name="project name" access="read,write,required"></variable>
                <variable name="applier id" access="read,write,required"></variable>
             </controller>
          </task>
          <transition name="ready for review " to="Review Project"></transition>
       </start-state>
      
       <task-node name="Review Project" signal="last">
          <task name="request review">
       <controller>
                <variable name="project name" access="read"></variable>
                <variable name="applier id" access="read"></variable>
             </controller>
             <assignment class="org.springmodules.workflow.jbpm31.JbpmHandlerProxy" config-type="bean">
              <targetBean>adminAssignmentHandler</targetBean>
          <factoryKey>jbpmConfiguration</factoryKey>
             </assignment>  
          </task>
          <transition name="review pass" to="Upload File"></transition>
          <transition name="review fail" to="End"></transition>
       </task-node>
      
       <task-node name="Upload File" signal="last">
          <task name="upload profile">
             <controller>
                <variable name="project name" access="read"></variable>
                <variable name="applier id" access="read"></variable>
                <variable name="project profile" access="read,write,required"></variable>
             </controller>
             <assignment class="org.springmodules.workflow.jbpm31.JbpmHandlerProxy" config-type="bean">
              <targetBean>applierAssignmentHandler</targetBean>
          <factoryKey>jbpmConfiguration</factoryKey>
             </assignment>  
          </task>
          <transition name="upload finish" to="Publish Project"></transition>
       </task-node>
      
       <task-node name="Publish Project" signal="last">
          <task name="publish profile">
             <controller>
                <variable name="project name" access="read"></variable>
                <variable name="applier id" access="read"></variable>
                <variable name="project profile" access="read"></variable>
             </controller>
            <assignment class="org.springmodules.workflow.jbpm31.JbpmHandlerProxy" config-type="bean">
              <targetBean>adminAssignmentHandler</targetBean>
          <factoryKey>jbpmConfiguration</factoryKey>
             </assignment>  
          </task>
          <transition name="finish" to="End"></transition>
          <transition name="upload again" to="Upload File"></transition>
       </task-node>
      
       <end-state name="End">
         <event type="node-enter">
          <script>
           <expression>
              System.out.println("Process End. ");
           </expression>
          </script>
        </event>
       </end-state>
      
    </process-definition>
    这次的定义强调了使用controller来管理task的变量,考虑到prp的需求,主要是人工任务,所以基本放弃swimlane,使用AssignmentHandler来push工作项,另外,也小试了jpdl中使用beanshell。值得一提的是signal="last"是规定流程中某一步的Task都完成了,流程才继续,还有其他几种形式,这里不多说了。

    接着简单地照搬了springmodules中jbpm的配置,applicaitionContext-jbpm.xml和jbpm.cfg.xml:
    其中applicationContext-jbpm.xml中配置了两个AssignmentHandler:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
     <bean id="propertyConfigurer"
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="location" value="jdbc.properties" />
     </bean>
     <bean id="dataSource"
      class="com.mchange.v2.c3p0.ComboPooledDataSource"
      destroy-method="close" singleton="true">
      <property name="driverClass"
       value="${jbpm.jdbc.driverClassName}" />
      <property name="jdbcUrl" value="${jbpm.jdbc.url}" />
      <property name="user" value="${jbpm.jdbc.username}" />
      <property name="password" value="${jbpm.jdbc.password}" />
      <property name="maxPoolSize" value="5" />
     </bean>
     <bean id="jbpmSessionFactory"
      class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="mappingLocations">
       <list>
        <value>classpath*:org/jbpm/**/*.hbm.xml</value>
       </list>
      </property>
      <property name="hibernateProperties">
       <props>
        <prop key="hibernate.dialect">
         ${hibernate.dialect}
        </prop>
        <prop key="hibernate.show_sql">
         ${hibernate.show_sql}
        </prop>
        <prop key="hibernate.cache.use_query_cache">
         ${hibernate.cache.use_query_cache}
        </prop>
        <prop key="hibernate.cache.provider_class">
         ${hibernate.cache.provider_class}
        </prop>
       </props>
      </property>
     </bean>
     <!--Hibernate TransactionManager-->
     <bean id="jbpmTransactionManager"
      class="org.springframework.orm.hibernate3.HibernateTransactionManager">
      <property name="sessionFactory" ref="jbpmSessionFactory" />
     </bean>
     <!-- Process Definition of simpleOrderFlow -->
     <bean id="projectWorkflow"
      class="org.springmodules.workflow.jbpm31.definition.ProcessDefinitionFactoryBean">
      <property name="definitionLocation"
       value="classpath:project.par/processdefinition.xml" />
     </bean>
     <!-- jBPM configuration -->
     <bean id="jbpmConfiguration"
      class="org.springmodules.workflow.jbpm31.LocalJbpmConfigurationFactoryBean">
      <property name="sessionFactory" ref="jbpmSessionFactory" />
      <property name="configuration" value="classpath:jbpm.cfg.xml" />
      <property name="processDefinitions">
       <list>
        <ref local="projectWorkflow" />
       </list>
      </property>
     </bean>
     <!-- jBPM template -->
     <bean id="jbpmTemplate"
      class="org.springmodules.workflow.jbpm31.JbpmTemplate">
      <constructor-arg index="0" ref="jbpmConfiguration" />
      <constructor-arg index="1" ref="projectWorkflow" />
     </bean>
     <bean id="adminAssignmentHandler"
      class="org.openepo.workflow.taskmgmt.AdminAssignmentHandler"
      autowire="byName">
      <property name="adminId" value="admin" />
     </bean>
     <bean id="applierAssignmentHandler"
      class="org.openepo.workflow.taskmgmt.ApplierAssignmentHandler"
      autowire="byName" />
    </beans>
    AssignmentHandler代码:
    AdminAssignmentHandler.java:
    package org.openepo.workflow.taskmgmt;
    import org.jbpm.graph.exe.ExecutionContext;
    import org.jbpm.taskmgmt.def.AssignmentHandler;
    import org.jbpm.taskmgmt.exe.Assignable;
    public class AdminAssignmentHandler implements AssignmentHandler {
        private static final long serialVersionUID = 1L;
        private String adminId;
     
        public String getAdminId() {
            return adminId;
        }
        public void setAdminId(String adminId) {
            this.adminId = adminId;
        }
        public void assign(Assignable assignable, ExecutionContext executionContext) throws Exception {
            System.out.println("Assign task to " + adminId);
            assignable.setActorId(adminId);
        }
    }
    ApplierAssignmentHandler.java
    package org.openepo.workflow.taskmgmt;
    import org.jbpm.graph.exe.ExecutionContext;
    import org.jbpm.taskmgmt.def.AssignmentHandler;
    import org.jbpm.taskmgmt.exe.Assignable;
    public class ApplierAssignmentHandler implements AssignmentHandler {
        private static final long serialVersionUID = 1L;
        public void assign(Assignable assignable, ExecutionContext executionContext) throws Exception {
      
             String actorId = (String)executionContext.getVariable("applier id");
             System.out.println("Assign task to " + actorId);
             assignable.setActorId(actorId);
        }
    }
    setActorId就是指定具体的人来完成任务.

    根据jbpm自带的mysql的script来建表,需要的准备工作就做好了。
     
    现在prp的工作流设计中,主要在oswrokflow的基础上二次开发了:1.工作项 2.工作流代理 3.必要的function和condition(使用了项目其它模块) 4.工作流监控(就是条件查询)。
     
    1,2. 使用springmodules的话,首先工作项不用特别设计了,因为jbpm天生就有TaskInstance的概念。
    自己写工作项,就可以在配置文件中就可以关联必要的url入口,来直接由工作项引导到action,action再和代理类Operator对应,从而屏蔽工作流的驱动细节。
    但是如果使用jbpm+springmodules的话,这种思路无疑增加了复杂性,一层又包一层,干脆就放弃对工作流细节的屏蔽,放弃工作流代理,直接利用jbpmTemplate根据actorId来findTaskInstances,然后根据TaskInstance的一些信息来对应到不同的service层方法,在manager里直接使用jbpmTemplate回调来驱动工作流
    这里有个问题没解决,拿到TaskInstance也就是工作项列表后,某个具体工作项一定是要对应到一个Action的,现在prp的做法正如上面说的很好地解决了这个问题,但是如果jbpm+springmodules,可能就要在AssignmentHandler里set一个url的variable,前台freemarker通过TaskInstance直接$出来
     
    下面是具体代码,考虑到一切从简,只是用了一个Test文件,堆砌代码来说明一下如何用springmodules的jbpmTemplate。
    public void createNewProcess() {
     jbpmTemplate.execute(new JbpmCallback() {
              public Object doInJbpm(JbpmContext jbpmContext) throws JbpmException {
                    ProcessInstance processInstance = jbpmContext.newProcessInstance("ProjectWorkflow");
                    TaskInstance taskInstance = processInstance.getTaskMgmtInstance().createStartTaskInstance();
                    System.out.println("requesting...");             
                    /*  Data Input Code, Example:  */
                    taskInstance.setActorId(applierId);
                    taskInstance.setVariable("project name", "SoftEden");
                    taskInstance.setVariable("applier id", applierId);
                    taskInstance.end();
                    jbpmContext.save(processInstance);
                    return null;
                }
            });
    }
    这个方法应该放在service层,作用是启动新的流程,这里只是简单地模拟变量输入。
    public void adminActions() {
         List taskInstances = jbpmTemplate.findTaskInstances(adminId);
         System.out.println("Task instances size: " + taskInstances.size());
         for(Iterator it = taskInstances.iterator(); it.hasNext();) {
             TaskInstance currentTask = (TaskInstance)it.next();
             String taskInstanceName = currentTask.getName();
             System.out.println(taskInstanceName);
             long taskInstanceId = currentTask.getId();

             if(taskInstanceName.equals("request review"))
                 review(taskInstanceId);
             else if(taskInstanceName.equals("publish profile"))
                 publish(taskInstanceId);
         }
    }
    这个方法是管理员方法,涉及到两部分,一部分是findTaskInstances,这一步应该在Action层中调用service层方法,返回工作项列表到前台,另一部分就是处理具体的某个工作项,根据TaskInstance的name选择不同的service层方法。下面就是方法:
    public void review(final long taskInstanceId) {
         jbpmTemplate.execute(new JbpmCallback() {
             public Object doInJbpm(JbpmContext jbpmContext) throws JbpmException {
                   
                 TaskInstance taskInstance = jbpmContext.loadTaskInstance(taskInstanceId);
                 String projectName = (String)taskInstance.getVariable("project name");
                 String actorId = (String)taskInstance.getVariable("applier id");
                 System.out.println("Reviewing...");
                 System.out.println("Project Name: " + projectName);
                 System.out.println("Actor Id: " + actorId);
                 /*  Review Code, Example:  */
                 if(evaluate(projectName,actorId))
                      taskInstance.end("review pass");
                 else
                      /*  Other dispose work  */
                      taskInstance.end("review fail");
                 return null;
            }
        });
    }
     
    public void publish(final long taskInstanceId) {
         jbpmTemplate.execute(new JbpmCallback() {
             public Object doInJbpm(JbpmContext jbpmContext) throws JbpmException {
                   
                 TaskInstance taskInstance = jbpmContext.loadTaskInstance(taskInstanceId);
                 String projectName = (String)taskInstance.getVariable("project name");
                 String actorId = (String)taskInstance.getVariable("applier id");
                 String projectProfile = (String)taskInstance.getVariable("project profile");
                 System.out.println("publishing...");
                 System.out.println("Project Name: " + projectName);
                 System.out.println("Actor Id: " + actorId);
                 System.out.println("Project Profile: " + projectProfile);
                 /*  Publish Code, Example:  */
                 if(checkProfile(projectProfile))
                      taskInstance.end("finish");
                 else
                      taskInstance.end("upload again");
                 return null;
            }
        });
    }
    这两个方法使用JbpmCallback来完成流程逻辑。值得一提的是2点:1.利用taskInstance的end(String)方法,来选择流程分支transition,代码非常清晰;2.findTaskInstances出来的TaskInstance是lazy的,所以应该传TaskInstanceId到doInJbpm方法里load完整的TaskInstance,不要觉得这不方便,其实这是很实用的。
     
    最后是上传文件的代码示例,没有什么特别的,也是两部分组成:列出工作项,处理工作项。
    public void upload() {
        List taskInstances = jbpmTemplate.findTaskInstances(applierId);
        System.out.println("Task instances size: " + taskInstances.size());
        for(Iterator it = taskInstances.iterator(); it.hasNext();) {
            TaskInstance currentTask = (TaskInstance)it.next();
            String taskInstanceName = currentTask.getName();
            System.out.println(taskInstanceName);
            final long taskInstanceId = currentTask.getId();
            jbpmTemplate.execute(new JbpmCallback() {
                 public Object doInJbpm(JbpmContext jbpmContext) throws JbpmException {
                  TaskInstance taskInstance = jbpmContext.loadTaskInstance(taskInstanceId);
                  System.out.println("uploading...");
                  /*  Data Input Code, Example:  */
                  taskInstance.setVariable("project profile", "Apply open souce.");
                  taskInstance.end();
                     return null;
                 }
             });
        }
    }
     
    总而言之,prp中是在Action中调用WorkItemManager来返回工作项列表,现在可以直接用jbpmTemplate或者封装其到service层来返回工作项列表;另外,暴露工作流细节到service层,方法中利用回调中完成业务逻辑和工作流逻辑。
     
    3. 必要的外设,如短消息,JMS,权限检查,这里没有demo,可以实现ActionHandler和DesicionHandler来添加,这里不赘述,只需利用prp里的相关模块,虽然jbpm已经包办了很多内容,比如jms,比如权限,但是毕竟已经把jbpm当作了插件来用,另外prp中各模块都已经很好地work了,没必要再花时间配置,以后再说了。
     
    4. 工作流监控部分,prp中封装了osworkflow的Query,使用回调来进行具体查询,比如针对某个状态的流程实例,但是jbpm里居然没有这样的API(真是崩溃),所以监控部分只有JbpmTemplate提供的单薄的findProcessInstances和findProcessInstance(Long)了。
     
    这里只是简单地说说如果prp中用jbpm替代方案是什么,jbpm中的另外一些特别的部分,比如Super State、Process Composite和Scheduler,也放在以后研究。
  • 本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
    打开APP,阅读全文并永久保存 查看更多类似文章
    猜你喜欢
    类似文章
    【热】打开小程序,算一算2024你的财运
    jbpm的入门1
    Jbpm用户指南翻译:第11章 任务管理
    Jbpm流程图显示
    jbpm的任务管理实现
    jBPM与业务系统集成-通过定制Task Instance等方式实现(转)_专业,快乐,生...
    Fire Workflow源码分析——(基本组成)(1)
    更多类似文章 >>
    生活服务
    热点新闻
    分享 收藏 导长图 关注 下载文章
    绑定账号成功
    后续可登录账号畅享VIP特权!
    如果VIP功能使用有故障,
    可点击这里联系客服!

    联系客服