首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

jBPM4引见

2013-01-21 
jBPM4介绍1.???jBPM4介绍1,??jBPM4.4使用Hibernate3.3.1作为引擎的持久框架。2,??BPM4.4共有18张表。2.???准

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项。

?

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

2.2.???准备jBPM开发环境

先新建Eclipse工程(Java Project)。

2.2.1.????添加jBPM的jar包

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的文件名称,需要做:1从JBPM_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 canbe obtained from the ProcessEngine which is build from a Configuration.?A?ProcessEngine?isthread safe and can be stored in a static member field.

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

ProcessEngine processEngine = newConfiguration().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 engineobjects 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有:ExecutionService与TaskService。

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.????获取个人任务列表:

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

?

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

????????????????????????????.assignee(userId)//

????????????????????????????.page(0,1000)//

????????????????????????????.list();

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

1,??TaskService().completeTask(taskId);

4.2.4.????获取流程变量

1,TaskService.getVariable(taskId,variableName);

2,ExecutionService.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,一个节点中可以指定一个或多个Transition(End节点中没有)

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

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

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

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

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

4.3.2.????活动(节点)

4.3.2.1.??????????预定义节点

?

?

说明?

Start

开始

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

End、EndError、EndCancel

结束节点

后两个表示不同的状态

Task

?

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

Decision

?

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

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

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

Fork、Join

分支与合并

测试时要注意:

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

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

State

状态

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

分配任务

1,??actor=#{String型的变量}

2,??AssignmentHandler,需要在<task>元素中写<assignment-handler style="line-height: 12.6pt;">a)?????????指定的类要实现AssignmentHandler接口

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

?

4.3.2.2.??????????自定义节点:Custom

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

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

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

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

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

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

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

??????????????3,ActivityExecution.take(StringtransitionName),使用指定的Transition离开

??????????????4,ActivityExecution.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 style="line-height: 12.6pt;">3,??事件类型:

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

b)????????<on>元素放在节点元素中,可以指定event为start或end,表示节点的进入与离开

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

d)????????在<transition>元素中直接写<event-listener style="line-height: 12.6pt;">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?中配置:<propertyname="hibernate.current_session_context_class">thread</property>

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

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

b)????????在?BaseDaoImpl?中增加:

????????????????????????i.??????????????getSession() { returnHibernateUtils.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 linkingjavax/el/ExpressionFactory class

???????atorg.apache.jsp.WEB_002dINF.jsp.UserAction.loginUI_jsp._jspInit(loginUI_jsp.java:39)

???????atorg.apache.jasper.runtime.HttpJspBase.init(HttpJspBase.java:52)

???????atorg.apache.jasper.servlet.JspServletWrapper.getServlet(JspServletWrapper.java:159)

???????atorg.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:329)

???????atorg.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)

???????atorg.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)

???????... 40 more

??????

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

???????有三个解决办法:

???????1,方法一:在MyEclipse的Preferences -> 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.?其他说明

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

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

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

4,以下都是String型:

???????1,deploymentId

???????2,processDefinitionId

???????3,executionId

???????4,taskId

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

·???????SEVERE (highest value)

·???????WARNING

·???????INFO

·???????CONFIG

·???????FINE

·???????FINER

·???????FINEST (lowest value)

1.ProcessEngine流程引擎

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

ProcessEngine processEngine = newConfiguration().buildProcessEngine();

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

ProcessEngine processEngine = newConfiguration()

??????.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部署流程

???Stringdeploymentid = repositoryService.createDeployment()

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

????.deploy();

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

<process name="Insuranceclaim" key="ICL">

...

</process>

3.删除流程定义

repositoryService.deleteDeployment(deploymentId);

4.最新的流程实例(查找?key为ICL的最新版本的流程定义,?然后在最新的流程定义里启动流程实例,当部署了一个新版本,?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", newFloat(763.74));

ProcessInstance processInstance =

????executionService.startProcessInstanceByKey("ICL",variables);

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

<state name="wait">

??<onevent="start">

????<event-listener/>

??</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.

Decision的expr是指流程下一个Transition的名称

Task的assignee指的是用户

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

?

热点排行