jBPM4介绍

 

 

1.   jBPM4介绍

1  jBPM4.4使用Hibernate3.3.1作为引擎的持久框架。

2  BPM4.4共有18张表。

2.   准备环境

2.1.   安装流程设计器插件(Graphical Process Designer

1  jBPM4.4包含了一个图形化设计流程的工具(GPD),它是eclipse插件,是用来设计jPDL

的图形化流程的,支持的版本为Eclipse3.5

2  插件所在的路径为:install/src/gpd/jbpm-gpd-site.zip

3  安装方法:在eclipse3.5中点击help->install new software->work with->add->Archive,选择

jbpm4插件的安装包install/src/gpd/jbpm-gpd-site.zip 进行安装。安装完成后进行重新启动Eclipse

。(在User Guide文档的2.11.2节中有详细说明)。

4  查看是否成功安装了插件:WindowàPreference中是否有Jboss jBPM项。

 

(通过实验,发现不可以使用把插件目录直接放到dropins下面的方法)

5  流程定义文件的xsd文件的路径为:JBPM_HOME/src/jpdl-4.3.xsd

2.2.   准备jBPM开发环境

先新建Eclipse工程(Java Project)。

2.2.1.    添加jBPMjar

1  JBPM_HOME/jbpm.jar(核心包)

2  JBPM_HOME/lib/目录中的所有jar包,以下jar包可以不添加:hsqldb.jar, postgresql.jar, mysql-connector-java.jar, servlet-api.jar, junit.jar。其中junit.jar一定不要添加,因为是3.8.2版本,与我们使用的junit4有冲突。

3  所使用的数据库对应的驱动的jar包。

2.2.2.    添加配置文件并修改配置文件内容

1  配置文件可以从JBPM_HOME/examples/src/中拷贝:jbpm.cfg.xml   logging.properties

jbpm.hibernate.cfg.xml jbpm.tx.hibernate.cfg.xml

2  修改logging.properties中的日志输出级别[t1] WARNING:修改配置

java.util.logging.ConsoleHandler.level=WARNING

3  修改jbpm.hibernate.cfg.xml中的数据库连接信息,如果使用MySql,使用的方言一定要是

org.hibernate.dialect.MySQL5InnoDBDialect。(如使用MySQLDialect,在流程实例结束时会抛

异常:com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Cannot delete or update

a parent row: a foreign key constraint fails (`jbpm44_20101028/jbpm4_execution`, CONSTRAINT

`FK_EXEC_INSTANCE` FOREIGN KEY (`INSTANCE_`) REFERENCES `jbpm4_execution`

(`DBID_`))。)

4  创建数据库表。使用Hibernate的自动建表,在jbpm.hibernate.cfg.xml中配置:

hibernate.hbm2ddl.auto=update

 

说明:如果要改变jbpm.hibernate.cfg.xml的文件名称,需要做:1JBPM_HOME/src/中拷贝jbpm.tx.hibernate.cfg.xml放到工程的src/下,然后进行修改。2修改jbpm.tx.hibernate.cfg.xml中的hibernate主配置文件的路径配置(指定的是相对于classpath的相对路径)。

3.   API与实体

3.1. 核心API 的说明

Interacting with jBPM occurs through services. The service interfaces can be obtained from the ProcessEngine which is build from a Configuration. A ProcessEngine is thread safe and can be stored in a static member field.

使用默认的配置文件生成Configuration并构建ProcessEngine

ProcessEngine processEngine = new Configuration().buildProcessEngine();

或是使用指定的配置文件(要放到classPath):

ProcessEngine processEngine = new Configuration()

      .setResource("my-own-configuration-file.xml")

      .buildProcessEngine();

 

jBPM所有的操作都是通过Service完成的,以下是获取Service的方式:

RepositoryService repositoryService = processEngine.getRepositoryService();

ExecutionService executionService = processEngine.getExecutionService();

TaskService taskService = processEngine.getTaskService();

HistoryService historyService = processEngine.getHistoryService();

ManagementService managementService = processEngine.getManagementService();

说明:jBPM自己有一个IOC容器,Process engine objects defined in the configuration can also be retrieved by type (processEngine.get(Class<T>)) or by name (processEngine.get(String))

 

各种Service的作用:

RepositoryService

管理流程定义

ExecutionService

执行管理,包括启动、推进、删除流程实例,设置变量等操作

TaskService

任务管理(任务的查询与完成等操作)

HistoryService

历史管理(执行完的数据管理)

IdentityService

jBPM的用户、组管理

ManagementService

 

 

3.2.   实体、实体之间的关系、表 的说明

实体

说明

对应表

 

Deployment

 

 

 

ProcessDefinition

流程定义

 

 

 

 

 

 

Transition

流转、连线

 

 

Activity

活动(对应jBPM3中的节点Node

 

 

ActivityBehaviour

 

 

 

ExternalActivityBehaviour

 

 

 

TaskActivity

 

 

 

 

 

 

 

Execution

 

 

 

ProcessInstance

流程实例

 

 

 

 

 

 

TaskDefinition

任务定义

 

 

Task

任务(对应jBPM3中的任务实例)

 

 

 

 

 

 

 

4.   系统的学习

4.1.   管理流程(定义)

所有的流程定义的操作都是使用RepositoryService实现的。

4.1.1.    部署流程定义

RepositoryService.createDeployment()

 

说明:

1  再调用.addResourceFromClasspath(resource); 可以调用多次以添加多个文件。文件重复添加也不会报错。

2  再调用.addResourceFromInputStream(resourceName, inputStream)添加一个文件(使用InputStream

3  .addResourcesFromZipInputStream(zipInputStream)添加一个zip文件,里面有文件夹也没关系。就相当于一次添加了多个文件

4  以上方法可以在一起调用。

5  一个文件添加多次,则都会存储,但只是一个流程定义。

6  如果name指定为中文,则key就会为一个汉字变为一个"_"

 

4.1.2.    查询流程定义

RepositoryService().createProcessDefinitionQuery()

 

说明:

1  调用.list()查询一个列表,或调用.uniqueResult()查询一个唯一的结果。

2  排序:.orderAsc(ProcessDefinitionQuery.PROPERTY_NAME),其中属性名为常量

 

要完成的功能:

1  查询所有

2  查询所有最新的版本(要先查询出所有,再自己处理,只保留最新的版本)

4.1.3.    删除流程定义

1  删除一个:RepositoryService.deleteDeployment(deploymentId),删除流程定义

2  删除一个:RepositoryService.deleteDeploymentCascade(deploymentId),删除流程定义,且删除关联的流程实例与历史信息

3  删除指定名称的所有版本:需要先查出来,再一一删除。

 

4.1.4.    获取文件内容

RepositoryService().getResourceAsStream(deploymentId, resourceName);

 

说明:

1  resourceName是相对路径,或是部署时指定的名称。

2  如部署时使用.addResourceFromClasspath("cn/itcast/test.jpdl.xml"),则获取时指定名称为:cn/itcast//test.jpdl.xml。如果是zip,则是在zip中的一个相对于根的相对路径。

 

4.1.5.    获取某节点的坐标

RepositoryService().getActivityCoordinates(processDefinitionId, activityName);

4.2.   执行流程(实例)

要使用到的Service有:ExecutionServiceTaskService

4.2.1.    启动流程实例

1  ExecutionService.startProcessInstanceById(processDefinitionId),使用唯一的pdId查询并启动

2  ExecutionService.startProcessInstanceById(processDefinitionId, variables),可设置变量

3  ExecutionService.startProcessInstanceByKey(processDefinitionKey),使用指定key(name)的最新的版本启动

4  ExecutionService.startProcessInstanceByKey(processDefinitionKey, variables),可设置变量

 

说明:流程实例创建后,直接就到开始节点后的第一个节点,不会在开始节点停留。

4.2.2.    获取个人任务列表:

方式1TaskService().findPersonalTasks(userId);

 

方式2TaskService().createTaskQuery()//

                            .assignee(userId)//

                            .page(0, 1000)//

                            .list();

4.2.3.    办理任务(完成任务)

1  TaskService().completeTask(taskId);

4.2.4.    获取流程变量

1TaskService.getVariable(taskId, variableName);

2ExecutionService.getVariable(executionId, variableName);

 

 

4.2.5.    拾取任务

1  TaskService.takeTask(taskId, userId),如果任务有assignee,则会抛异常。

2  processEngine.getTaskService().assignTask(taskId, userId),转交任务

 

4.3. 设计流程(画流程图)

4.3.1.    Transition

1,一个节点中可以指定一个或多个TransitionEnd节点中没有)

2,如果只有一个,则可以不指定名称

3,如果有多个,则要分别指定唯一的名称

4signal时,如果没有指定signalName/transitionName,则代表要使用没有名称的Transition,如果没有,就报错。

5signal时,可以传递一个参数signalName,代表使用指定的Transition离开,如果没有,就报错。

6,完成任务时,completeTask()将使用默认的TransitoncompleteTask(String)将使用指定的Transition

4.3.2.    活动(节点)

4.3.2.1.          预定义节点

 

 

说明 

Start

开始

启动流程后会自动离开开始节点

EndEndErrorEndCancel

结束节点

后两个表示不同的状态

Task

 

可以不指定assignee,也不会向下执行,而是等待。

Decision

 

1,使用expression,如:expr="#{'to state2'}"

2,使用Handler,要实现DecisionHandler接口

3,如果同时配置了expressionHandler,则expression有效,忽略Handler

ForkJoin

分支与合并

测试时要注意:

1pi.findActiveActivityNames()可以使用,但要保证是最新状态,也就是要先getById一下。

2,流程实例执行完后,用ExecutionService就查不到了

State

状态

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分配任务

1  actor=#{String型的变量}

2  AssignmentHandler,需要在<task>元素中写<assignment-handler class="AssignmentHandlerImpl"/>子元素。

a)         指定的类要实现AssignmentHandler接口

b)        在其中可以使用Assignable.setAssignee(String),分配个人任务。

 

4.3.2.2.          自定义节点:Custom

1,在<custom>元素中指定class属性为指定的类。

2,这个类要实现ExternalActivityBehaviour接口,其中有两个方法:

       1execute(ActivityExecution),节点的功能代码

       2signal(ActivityExecution, String, Map),在当前节点等待时,外部发信号时的行为

       3,在execute()方法中,可以调用以下方法对流程进行控制

              1ActivityExecution.waitForSignal(),在当前节点等待。

              2ActivityExecution.takeDefaultTransition(),使用默认的Transition离开,当前节点中定义的第一个为默认的。

              3ActivityExecution.take(String transitionName),使用指定的Transition离开

              4ActivityExecution.end(),结束流程实例

       4,也可以实现ActivityBehaviour接口,只有一个方法execute(ActivityExecution),这样就不能等待,否则signal时会有类转换异常。

4.3.3.    事件

4.3.3.1.          事件种类(有多少种事件)

4.3.3.2.          为事件配置动作(事件触发时做什么事)

1  在根元素中,或在节点元素中,使用<on event="">元素指定事件,其中event属性代表事件的类型。

2  <on>中用子元素<event-listener class="EventListenerImpl" />,指定处理的类,要求指定的类要实现EventListener接口

3  事件类型:

a)         <on>元素放在根元素(<process>)中,可以指定eventstartend,表示流程的开始与结束。

b)        <on>元素放在节点元素中,可以指定eventstartend,表示节点的进入与离开

c)         Start节点中只有end事件,在End节点中只有start事件。

d)        <transition>元素中直接写<event-listener class="">,就是配置事件。(因为在这里只有一个事件,所以不用写on与类型)

e)         <task>元素中还可以配置assign事件,是在分配任务时触发的。

5.   附录

5.1.   常见问题说明

5.1.1.    自行控制事务

4  修改 jbpm.tx.hibernate.cfg.xml

a)         不自行管理事务:去掉<standard-transaction-interceptor />

b)        Jbpm使用SessionFactory.getCurrentSession():修改为 <hibernate-session current="true" />

5  配置可以使用SessionFactory.getCurrentSession(),在jbpm.hibernate.cfg.xml 中配置:<property name="hibernate.current_session_context_class">thread</property>

6  要使用同一个SessionFactory,且都要使用 SessionFactory.getCurrentSession() 获取 Session

a)         同一个SessionFactorySessionFactory sf = processEngine.get(SessionFactory.class)

b)         BaseDaoImpl 中增加:

                        i.              getSession() { return HibernateUtils.getSessionFactory().getCurrentSession(); }

                      ii.              getProcessEngine(){ return org.jbpm.api.Configuration.getProcessEngine(); }

7  统一的打开与提交或回滚事务:使用 OpenSessionInViewFilter 控制事务。

 

5.1.2.    启动Tomcat时(使用的是MyEclipse自带的Tomcat,是6.0的版本),报错:  Caused by: java.lang.LinkageError: loader constraints violated when linking javax/el/ExpressionFactory class

       at org.apache.jsp.WEB_002dINF.jsp.UserAction.loginUI_jsp._jspInit(loginUI_jsp.java:39)

       at org.apache.jasper.runtime.HttpJspBase.init(HttpJspBase.java:52)

       at org.apache.jasper.servlet.JspServletWrapper.getServlet(JspServletWrapper.java:159)

       at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:329)

       at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)

       at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)

       ... 40 more

      

       说明:原因是Jbpmjuel.jar, juel-engine.jar, juel-impl.jar包和Tomcat6.0中的el-api.jar包冲突了。

       有三个解决办法:

       1,方法一:在MyEclipsePreferences -> MyEclipse -> Application Servers -> Tomcat -> .. -> Paths 中配置Append to classpath

              选中 juel.jar, juel-engine.jar, juel-impl.jar 这三个jar包就可以了。

       2,方法二:将 juel.jar, juel-engine.jar, juel-impl.jar 这三个包复制到tomcat6 lib/ 中,并删除原来的el-api.jar

              切记还要把工程中 WEB-INF\lib 下的 juel.jar, juel-engine.jar, juel-impl.jar 删除,不然还是要冲突。

       3,方法三:换成tomcat5.5,就没有问题了。

      

==========================

5.1.3.    完成流程实例中的最后一个任务时(任务实例结束时),或删除流程定义级联删除流程实例时,报错如下:

com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`/jbpm4_execution`, CONSTRAINT `FK_EXEC_INSTANCE` FOREIGN KEY (`INSTANCE_`) REFERENCES `jbpm4_execution` (`DBID_`))

 

解决办法:把方言设为 MySQL5InnoDBDialect,不能是 MySQLDialect

 

 

5.2. 其他说明

1Jbpm4的所有实体的数据库中用的主键都long型。

2,各种Service中的查询为createXxQuery(),调用list()查询一个列表,或调用uniqueResult()查询一个唯一的结果

3,查询支持分页,方法为:xxQuery.count()  xxQuery.page(int, int).list()

4,以下都是String型:

       1deploymentId

       2processDefinitionId

       3executionId

       4taskId


 [t1]java.util.logging.Leveljavadoc中列出了可配置的所有输出级别为(由高到低):

·       SEVERE (highest value)

·       WARNING

·       INFO

·       CONFIG

·       FINE

·       FINER

·       FINEST (lowest value)

1.ProcessEngine流程引擎

通过classpath根目录下 默认的配置文件jbpm.cfg.xml创建一个ProcessService

ProcessEngine processEngine = new Configuration().buildProcessEngine();

指定其他位置的配置文件, 请使用setResource()方法

ProcessEngine processEngine = new Configuration()

      .setResource("my-own-configuration-file.xml")

      .buildProcessEngine();

根据流程引擎可以得到下面的服务

RepositoryService repositoryService = processEngine.getRepositoryService();

ExecutionService executionService = processEngine.getExecutionService();

TaskService taskService = processEngine.getTaskService();

HistoryService historyService = processEngine.getHistoryService();

ManagementService managementService = processEngine.getManagementService();

 

2.Deploying a process部署流程

   String deploymentid = repositoryService.createDeployment()

    .addResourceFromClasspath("org/jbpm/examples/services/Order.jpdl.xml")

    .deploy();

如没有定义流程文件的key的时候自动取name(建议设定流程的key

<process name="Insurance claim" key="ICL">

...

</process>

3.删除流程定义

repositoryService.deleteDeployment(deploymentId);

4.最新的流程实例(查找 keyICL的最新版本的流程定义, 然后在最新的流程定义里启动流程实例,当部署了一个新版本, startProcessInstanceByKey方法会自动切换到 最新部署的版本

ProcessInstance processInstance = executionService.startProcessInstanceByKey("ICL");

5.根据特定的版本启动流程实例,可以使用流程定义的id启动流程实例

ProcessInstance processInstance =

    executionService.startProcessInstanceById("ICL-1");

6.新启动的流程实例分配一个key(业务key)(建议定义一个key

ProcessInstance processInstance =

    executionService.startProcessInstanceByKey("ICL", "CL92837");

7.如果没有提供用户定义的key,数据库就会把主键作为key 这样可以使用如下方式获得id:(不建议使用搜索,太耗资源)

ProcessInstance processInstance =

    executionService.startProcessInstanceByKey("ICL");

String pid = processInstance.getId();

8.使用变量(当一个新的流程实例启动时就会提供一组对象参数。 将这些参数放在variables变量里, 然后可以在流程实例创建和启动时使用

Map<String,Object> variables = new HashMap<String,Object>();

variables.put("name", "tension");

variables.put("type", "Accident");

variables.put("amount", new Float(763.74));

ProcessInstance processInstance =

    executionService.startProcessInstanceByKey("ICL", variables);

9.获得正确的执行的方法(给state活动分配一个事件监听器)

<state name="wait">

  <on event="start">

    <event-listener class="org.jbpm.examples.StartExternalWork" />

  </on>

</state>

在这个时间监听器里,你也可以通过execution.getId()获得确切的流程id,执行完后需要signal继续运行

executionService.signalExecutionById(executionId);

10.获得用户的任务列表

List<Task> taskList = taskService.findPersonalTasks("tension");

11.读取任务数据variables

Set<String> variableNames = taskService.getVariableNames(taskId);

variables = taskService.getVariables(taskId, variableNames);

12.写入任务数据variables

variables = new HashMap<String, Object>();

variables.put("category", "small");

variables.put("lires", 923874893);

taskService.setVariables(taskId, variables);

13.完成任务

taskService.completeTask(taskId);

taskService.completeTask(taskId, variables);

taskService.completeTask(taskId, outcome);

taskService.completeTask(taskId, outcome, variables);

//Outcome:决定哪个外出转移会被选中,逻辑如下

  如果一个任务拥有一个没用名称的外向转移:

·         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'的转移。

14.查找某一特定流程定义的所有流程实例

List<HistoryProcessInstance> historyProcessInstances = historyService

  .createHistoryProcessInstanceQuery()

  .processDefinitionId("ICL-1")

  .orderAsc(HistoryProcessInstanceQuery.PROPERTY_STARTTIME)

  .list();

或者单独的活动

List<HistoryActivityInstance> histActInsts = historyService

    .createHistoryActivityInstanceQuery()

    .processDefinitionId("ICL-1")

    .activityName("a")

    .list();

15.获得所有已经执行的节点列表

List<HistoryActivityInstance> histActInsts = historyService

    .createHistoryActivityInstanceQuery()

    .processInstanceId("ICL.12345")

    .list();

上面的查询与通过execution id查询有一些不同。有时execution id和流程实例id是不同的, 当一个节点中使用了定时器,execution id中就会使用额外的后缀, 这就会导致当我们通过execution id查询时, 这个节点不会出现在结果列表中。

16.返回指定流程定义的所有流程实例并分页

List<ProcessInstance> results = executionService.createProcessInstanceQuery()

                                       .processDefinitionId("my_process_definition")

                                       .notSuspended()

                                       .page(0, 50)

                                       .list();

17.获得所有查询任务并分页

List<Task> myTasks = taskService.createTaskQuery()

    .processInstanceId(piId)

    .assignee("tension")

    .page(100, 120)

    .orderDesc(TaskQuery.PROPERTY_DUEDATE)

    .list();

 

18.

Decisionexpr是指流程下一个Transition的名称

Taskassignee指的是用户

Java 需要指定具体的哪个类和方法,可带值,也可以返回值

 

你可能感兴趣的:(jbpm4)