1、开发环境搭建
1.1必须安装的软件
1) jbpm-4.4.zip
2) jdk 5或者更高版本
3) 支持的数据库有hsqldb、mysql、oracle、postgresql、sybase,本说明以oracle作为数据库。
4) GPD插件(jbpm在IDE中的流程设计器), eclipse(建议3.6版本以上)或者myeclipse(建议8.1版本以上)安装插件所需的zip在解压后jbpm-4.4\install\src\gpd中的jbpm-gpd-site.zip,本说明以eclipse3.6为例。
1.2配置JBPM运行时
1) 打开window à preferences
2) 选择 JBoss jBPM --> jBPM 4 --> Runtime Locations
3) 点击 Add...
4) 在 Add Location 对话框中,输入一个名字,比如 jbpm-4.0 然后点击 Search...
5) 在 Browse For Folder 对话框中,选择你的jbpm 根目录,然后点击 OK
6) 点击 OK 在 Add Location 对话框中
1.3定义jBPM 用户库
1) 点击窗口 --> 属性(Windows --> Preferences)
2) 选择Java --> 创建路径 --> 用户类库(Java --> Build Path --> User Libraries)
3) 点击新建(New)
4) 类型名字jBPM Libraries
5) 点击添加JARs(Add JARs...)
6) 找到jBPM 安装程序下的lib 目录
7) 选择lib 下的所有jar 文件并点击打开(Open)
8) 选择jBPM Libraries 作为入口
9) 重新点击添加JARs(Add JARs)
10) 在jBPM 的安装程序的根目录下选择jbpm.jar 文件
11) 点击打开(Open)
12) 在jbpm.jar 下选择源码附件(Source attachment)作为入口
13) 点击编辑(Edit)
14) 在源码附件的配置(Source Attachment Configuration)对话框中,点击目录(External Folder...)
15) 找到jBPM 安装程序下的src 目录
16) 点击选择(Choose)
17) 点击两次'确定'(Ok)会关闭所有对话框
1.4在目录中添加jPDL4 模式
如果你想直接编辑XML 源码,最好是在你的XML 目录中指定一下模式(schema),这样当你在编辑流程源码的时候,可以更好的帮助你编写代码。
1) 点击窗口 --> 属性(Windows --> Preferences)
2) 选择XML --> 目录(XML --> CataLog)
3) 点击添加(Add)
4) 添加XML 目录(Add XML Catalog Entry)的窗口打开
5) 点击map-icon 的图标下面的按钮并选择文件系统(File System)
6) 在打开的对话框中,选择jBPM 安装目录下src 文件夹中jpdl.xsd 文件
7) 点击打开(Open)并且关闭所有的对话框
2、实例教程讲解
这节我们将使用建立一个简单请假流程项目
2.1 建立项目
在eclipse中新建一个Dynamic Web Project的项目jbpm4leave。
2.2 加入jbpm用户库
1)邮件点击新建的项目jbpm4leave—>Properties
2)如下图说示加入用户库
2.3 加入jbpm所需要的配置文件
大家可以从jbpm-4.4解压后的文件下,在路径\examples\src中找到以下文件,加入到项目工程的src中
其中jbpm.hibernate.cfg.xml是配置jbpm4.4的hibernate配置文件,包括数据源的配置,和一般的hibernate.cfg.xml配置文件差不多。Jbpm.mailkit开头的文件,是用于邮件功能的配置。
2.4 新建简单的请假流程
1)新建一个jbpm4.4的流程定义文件
右键点击srcàNewàOther,选择JBoss jBPM下的Jbpm 4 Process Definition,文件名写leave,版本号写4.4即可。
3)用流程设计器打开新建的leave.jpdl.xml。
右键点击leave.jpdl.xmlàOpen WithàjBPM jPDL4 Editor,,看见如下效果
3)设计请假流程
在这个简单的请假流程中,包含开始(start)、结束(end)、任务(task)、决策(decision)四种流程元素。
流程设计如下:
4)详细设计流程中的各任务节点的流转条件
①选中“申请”任务节点,在properties(属性)中,General标签的Name(属性值)填为“申请”,Assignment标签的Type选择 assignee(参与者,选择这个,可以指定该任务节点的直接参与者),Expression的属性值填为#{owner}(即指定这个任务节点的直接参与者就是这个流程的启动者)。
②选中“经理审批”任务节点,在properties(属性)中,General标签的Name(属性值)填为“经理审批”,Assignment标签的Type选择 candidate-groups(参与者,选择这个,可以该任务节点有某个用户组来参与),Expression的属性值填为manager(即指定这个任务节点的有manager这个用户组来完成)。
③“老板审批”节点同“经理审批”任务节点设置类似,只是Expression改为boss
④设置决策节点,选中,在属性标签General中按如下所示填写:
Name(节点名称),Expression(判断表达式,其中day是在流程流转过程中的一个记录天数的属性,整个表达式的意思是天数大于3天需要老板审批,如果天数不大于3天,经理审批通过后就直接结束流程,不需要老板审批)。
⑤设计好流程后,点击保存,IDE将自动为你生成一个png的流程图片,切记不可在xml源码界面进行保存,否则会生成不完整的流程图片。
⑥在点击下方的“Source”属性,即可看到流程定义文件的xml源码
一下是leave.jpdl.xml的源码:
<?xml version="1.0" encoding="UTF-8"?>
<process name="leave" xmlns="http://jbpm.org/4.4/jpdl">
<start g="198,15,48,48" name="start1">
<transition to="申请"/>
</start>
<task assignee="#{owner}" form="request.jsp" g="175,94,92,52" name="申请">
<transition to="经理审批"/>
</task>
<task candidate-groups="manager" form="manager.jsp" g="176,174,92,52" name="经理审批">
<transition g="-32,-8" name="批准" to="exclusive1"/>
<transition g="120,199;121,122:-42,-18" name="驳回" to="申请"/>
</task>
<decision expr="#{day > 3 ? '老板审批' : '结束'}" g="196,259,48,48" name="exclusive1">
<transition g="-39,-10" name="结束" to="end1"/>
<transition g="344,283:-71,-17" name="老板审批" to="老板审批"/>
</decision>
<task candidate-groups="boss" form="boss.jsp" g="299,305,92,52" name="老板审批">
<transition g="342,378:" to="end1"/>
</task>
<end g="196,354,48,48" name="end1"/>
</process>
至此工程的src文件下就有下面这些文件:
2.5 发布流程
Jbpm的流程发布其实很简单,只要使用jbpm已经封装好的方法进行使用,即可。
我们新建一个deploy.jsp的页面用户发布流程。在此讲解使用zip文件打包发布流程。
1) 将之前建立好的leave.jpdl.xml和leave.png文件,一起打包进leave.zip文件。
2) deploy.jsp代码如下:
<%@ page language="java" contentType="text/html; charset=gb2312"%>
<%@page
import="java.io.File,java.io.FileInputStream,java.io.InputStream,java.util.zip.ZipInputStream,java.util.*,org.jbpm.api.*,java.util.zip.*"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<%
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
String deployFile = request.getParameter("processDef");
if (deployFile != null && deployFile != "") {
//必须使用的,流程引擎
ProcessEngine processEngine = Configuration.getProcessEngine();
RepositoryService repositoryService = processEngine
.getRepositoryService();
try {
String file = deployFile;
//将ZIP文件封转到IO流里
InputStream is = new FileInputStream(new File(file));
ZipInputStream zis = new ZipInputStream(is);
//将ZIP流程文件发布到pvm(流程虚拟机中,他会把ZIP包中的xml文件和png图片存储到数据库中)
repositoryService.createDeployment()
.addResourcesFromZipInputStream(zis).deploy();
zis.close();
is.close();
out.println("发布流程成功<br />");
out.println("<a href='task-write.jsp'>返回</a><br />");
} catch (Exception e) {
e.printStackTrace();
out.println("发布流程失败");
}
}
%>
<script type="text/javascript">
function readFile(obj) {
document.getElementById("file").value = obj.value;
}
</script>
<font color="red">可直接发布zip文件</font><br />
<form name="form1" method="post" action="deploy.jsp"><label>
发布流程文件<input type="file" name="processDef" onchange="readFile(this)">
</label><label><input type="submit" name="Submit" value="提交">
</label></form>
</body>
</html>
3)测试发布成功结果
4)查看已发布成功的流程
已经有的一个查看流程的页面task-write.jsp,源码如下:
<%@ page language="java" contentType="text/html; charset=GB18030"
pageEncoding="GB18030"%>
<%@include file="/head.jsp"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
String username = (String) session.getAttribute("username");
//流程引擎
ProcessEngine processEngine = Configuration.getProcessEngine();
RepositoryService repositoryService = processEngine
.getRepositoryService();
//流程定义集合
List<ProcessDefinition> pdList = repositoryService
.createProcessDefinitionQuery().list();
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB18030">
<title>Insert title here</title>
<style type="text/css">
body {
font: normal 11px auto "Trebuchet MS", Verdana, Arial, Helvetica,
sans-serif;
color: #4f6b72;
background: #E6EAE9;
}
a {
color: #c75f3e;
}
#mytable {
padding: 0;
margin: 0;
}
caption {
padding: 0 0 5px 0;
width: 700px;
font: italic 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
text-align: right;
}
th {
font: bold 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #4f6b72;
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
border-top: 1px solid #C1DAD7;
letter-spacing: 2px;
text-transform: uppercase;
text-align: left;
padding: 6px 6px 6px 12px;
background: #CAE8EA;
}
th.nobg {
border-top: 0;
border-left: 0;
border-right: 1px solid #C1DAD7;
background: none;
}
td {
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
background: #fff;
font-size: 11px;
padding: 6px 6px 6px 12px;
color: #4f6b72;
}
td.alt {
background: #F5FAFA;
color: #797268;
}
th.spec {
border-left: 1px solid #C1DAD7;
border-top: 0;
background: #fff;
font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
}
th.specalt {
border-left: 1px solid #C1DAD7;
border-top: 0;
background: #f5fafa;
font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #797268;
}
body.td {
font-size: 11px;
}
</style>
</head>
<body>
<TABLE width="100%" id="mytable" id="mytable">
<caption>流程定义</caption>
<thead>
<tr>
<th class="spec">流程id</th>
<th>流程名称</th>
<th>版本号</th>
<th>流程删除</th>
<th>启动流程</th>
</tr>
</thead>
<tbody>
<%
for (ProcessDefinition pd : pdList) {
%>
<tr>
<th class="spec"><%=pd.getId()%></th>
<td class="alt"><%=pd.getName()%></td>
<td class="alt"><%=pd.getVersion()%></td>
<td class="alt"><a href="remove.jsp?id=<%=pd.getDeploymentId()%>">删除流程</a></td>
<td class="alt"><a href="start.jsp?id=<%=pd.getId()%>">启动流程</a></td>
</tr>
<%
}
%>
</tbody>
</table>
</body>
</html>
5)查看流程发布情况
6)流程定义所设计到的表
JBPM4_DEPLOYMENT
JBPM4_DEPLOYPROP //存放流程定义的版本号,使用的jbpm版本号,已经流程名
JBPM4_JOB //存放timer的定义
JBPM4_LOB //存放流程定义的xml和png图片文件
2.6 启动流程
1)启动流程,其实很简单,需要获得流程定义的ID,使用jbpm已经封装好的流程启动方法就可以了。
//start.jsp
<%@page import="java.util.*,org.jbpm.api.*,java.util.*"%>
<%
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
try{
//流程引擎
ProcessEngine processEngine = Configuration.getProcessEngine();
//流程实例服务
ExecutionService executionService = processEngine
.getExecutionService();
Map map = new HashMap();
//插入流程中存放的数据,这里存放流程启动者的名字,参数id是流程定义的ID
map.put("owner", session.getAttribute("username"));
executionService.startProcessInstanceById(request
.getParameter("id"), map);
response.sendRedirect("task-personal-list.jsp");
}catch(Exception e){
e.printStackTrace();
}
%>
2)查看代办任务
// task-personal-list.jsp
<%@ page language="java" contentType="text/html; charset=GB18030"
pageEncoding="GB18030"%>
<%@page import="org.jbpm.api.*,org.jbpm.api.task.*"%>
<%@include file="/head.jsp"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
String username = (String) session.getAttribute("username");
//流程引擎
ProcessEngine processEngine = Configuration.getProcessEngine();
//任务引擎
TaskService taskService = processEngine.getTaskService();
//当前用户代办的任务集合
List<Task> taskList2 = taskService.findPersonalTasks(username);
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB18030">
<title>Insert title here</title>
<style type="text/css">
body {
font: normal 11px auto "Trebuchet MS", Verdana, Arial, Helvetica,
sans-serif;
color: #4f6b72;
background: #E6EAE9;
}
a {
color: #c75f3e;
}
#mytable {
padding: 0;
margin: 0;
}
caption {
padding: 0 0 5px 0;
width: 700px;
font: italic 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
text-align: right;
}
th {
font: bold 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #4f6b72;
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
border-top: 1px solid #C1DAD7;
letter-spacing: 2px;
text-transform: uppercase;
text-align: left;
padding: 6px 6px 6px 12px;
background: #CAE8EA;
}
th.nobg {
border-top: 0;
border-left: 0;
border-right: 1px solid #C1DAD7;
background: none;
}
td {
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
background: #fff;
font-size: 11px;
padding: 6px 6px 6px 12px;
color: #4f6b72;
}
td.alt {
background: #F5FAFA;
color: #797268;
}
th.spec {
border-left: 1px solid #C1DAD7;
border-top: 0;
background: #fff;
font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
}
th.specalt {
border-left: 1px solid #C1DAD7;
border-top: 0;
background: #f5fafa;
font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #797268;
}
body.td {
font-size: 11px;
}
</style>
</head>
<body>
<table width="100%" id="mytable" id="mytable">
<caption>个人待办任务</caption>
<thead>
<tr>
<th class="spec">流程ID</th>
<th>当前节点</th>
<th>查看详细信息</th>
<th>查看流程图</th>
</tr>
</thead>
<tbody>
<%
for (Task task : taskList2) {
%>
<tr>
<td class="alt"><%=task.getId()%></td>
<td class="alt"><%=task.getName()%></td>
<td class="alt"><a
href="<%=task.getFormResourceName()%>?id=<%=task.getId()%>">查看详细信息</a></td>
<td class="alt"><a href="view.jsp?id=<%=task.getExecutionId() %>">查看流程图</a></td>
</tr>
<%
}
%>
</tbody>
</table>
</body>
</html>
3)实现如图:
//流程列表
//流程启动后,成为当前用户的待办任务
2.7 流程办理(申请)
由于我们的流程如下,启动启动流程后,流程就进入了“申请”这个流程节点(注:流程启动后,会在流程第一个节点所有人/所有组的代办事项中找到)。
所以,现在就要在申请中进行办理。由于我们在定义流程时如下定义了:
<task assignee="#{owner}" form="request.jsp" g="175,94,92,52" name="申请">
<transition to="经理审批"/>
</task>
定义了,“申请”这个节点是有流程启动者进行办理的,form指定办理“申请”这个节点的处理页面,用户可以自己定义处理页面。Transition指定申请流程的下一个出口。
1)在代办事项中,我们有一行如下代码:
<a href="<%=task.getFormResourceName()%>?id=<%=task.getId()%>">查看详细信息</a>
在这里会传一个任务ID,所以在request.jsp中必须要取得这个任务ID。
//request.jsp源码
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
%>
<fieldset>
<legend>申请</legend>
<form method="post" onsubmit="return validate()">
<input type="hidden" name="taskId" value="${param.id}">
申请人:<input type="text" name="owner" value="${sessionScope['username']}"/><br/>
请假时间:<input type="text" name="day" value="" onchange="change(this)"/><br/>
请假原因:<textarea name="reason"></textarea><br/>
<input type="submit"/>
</form>
</fieldset>
<script type="text/javascript">
function change(obj){
var reg = /^[1-9]\d*$/;
if (!reg.test(obj.value))
{
alert("天数必须为正整数!");
obj.value="";
obj.focus();
returnfalse;
}
}
function validate(){
var obj = document.getElementsByName("day")[0].value;
var reg = /^[1-9]\d*$/;
if (!reg.test(obj))
{
alert("天数必须为正整数!");
document.getElementsByName("day")[0].value="";
document.getElementsByName("day")[0].focus();
returnfalse;
}else{
document.forms[0].action="submit.jsp";
document.forms[0].submit();
returntrue;
}
}
</script>
</body>
</html>
前台显示如下:
填写完内容后,页面将会提交到submit.jsp,将对提交的数据进行处理,同时流程继续流转,所以在提交的的页面中要处理两件事情:
1、处理用户提交数据,将数据和流程实例进行绑定
2、将流程继续流转,提交给下一个节点办理
//submit.jsp源码如下
<%@page import="java.util.*,org.jbpm.api.*"%>
<%@page import="java.sql.*" %>
<%
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
%>
<%
ProcessEngine processEngine = Configuration.getProcessEngine();
TaskService taskService = processEngine.getTaskService();
String username = (String) session.getAttribute("username");
String taskId = request.getParameter("taskId");
String owner = request.getParameter("owner");
int day = Integer.parseInt(request.getParameter("day"));
String reason = request.getParameter("reason");
String sDBDriver = "oracle.jdbc.driver.OracleDriver";
String sConnStr = "jdbc:oracle:thin:@10.142.7.9:1521:orcl";
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try
{
Class.forName(sDBDriver);
conn = DriverManager.getConnection(sConnStr,"scott","scott");
conn.setAutoCommit(false);
//查询待办事项列表
String sql1 = "UPDATE jbpm4_hist_task task set task.assignee_=? where task.dbid_=?";
stmt = conn.prepareStatement(sql1);
stmt.setString(1,username);
stmt.setString(2,taskId);
stmt.executeUpdate();
conn.commit();
}catch(Exception e){
e.printStackTrace();
try{
conn.rollback();
}catch(Exception e1){
e1.printStackTrace();
}
}finally{
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
}
//将数据存储封转到map中
Map map = new HashMap();
map.put("day", day);
map.put("reason", reason);
//将数据和流程进行绑定,具体请看jbpm4.4API
//绑定数据的同时,将现在的任务节点completeTask,既完成当前操作,将流程向下提交
taskService.completeTask(taskId, map);
response.sendRedirect("task-personal-list.jsp");
%>
=========我们这里将请假的内容填写如下=============
请假四天,根据流程定义,大于四天经理审批通过后,还要老板审批才可以。
2.8 流程办理(经理审批)
请假“申请”提交后,就进入经理审批流程了。在之前的经理审批节点,我们进行如下定义:
<task form="manager.jsp" g="175,217,92,52" candidate-groups="manager"
name="经理审批">
<transition g="-32,-8" name="批准" to="exclusive1" />
<transition g="128,221;124,165:-42,-18" name="驳回" to="申请" />
</task>
注:form:处理经理审批的处理页面
Candidate-groups:处理当前任务节点的用户组,如果不定义则任务都在这里节点进行 操作,还可以像“申请”节点一样,定义assignee的属性,但是candidate-groups 和assigness同时只能使用其中的一个。
这里定义了两个transition,分别是“批准”和“驳回”,决定的流程的下一个流转节点 是哪里。在具体操作中,将详细说明。
//manager.jsp源码如下:
其中,有两点需要注意:
1)taskService.getVariable(taskId, "owner")这样的内容是,取出之前在“申请”节点中,封装进流程实例的相应变量的值,如果你要对已有值进行修改或者要增加新值,只需要对你当前想要变更的变量封装进map,然后同“申请”一样绑定到流程实例中即可。
2)在页面中给出了两个submit提交按钮,他们的name相同,但是value不同:
<input name="result" type="submit" value="批准" />
<input name="result" type="submit" value="驳回" />
这刚好和我们之前定义的流程的两个transition相同,他是为后面流转流程所做服务的。
<%@ page language="java" contentType="text/html; charset=gb2312"%>
<%@page import="org.jbpm.api.*,org.jbpm.api.task.*"%>
<%@page import="java.sql.*"%>
<%
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
ProcessEngine processEngine = Configuration.getProcessEngine();
TaskService taskService = processEngine.getTaskService();
String taskId = request.getParameter("id");
Task task = taskService.getTask(taskId);
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<fieldset><legend>经理审核</legend>
<form action="submit_manager.jsp" method="post"><input
type="hidden" name="taskId" value="${param.id}">申请人:<%=taskService.getVariable(taskId, "owner")%><br />
请假时间:<%=taskService.getVariable(taskId, "day")%><br />
请假原因:<%=taskService.getVariable(taskId, "reason")%><br />
<input name="result" type="submit" value="批准" />
<input name="result" type="submit" value="驳回" /><br />
<a href="transfer.jsp?taskId=${param.id}">转办</a></form>
</fieldset>
<br />
<table border="1" width="100%">
<caption>当前实例办理情况</caption>
<thead>
<tr>
<td>流程ID</td>
<td>节点名称</td>
<td>开始时间</td>
<td>结束时间</td>
<td>参与人员</td>
</tr>
</thead>
<tbody>
<%
String sDBDriver = "oracle.jdbc.driver.OracleDriver";
String sConnStr = "jdbc:oracle:thin:@10.142.7.9:1521:orcl";
Connection conn = null;
ResultSet rs = null;
PreparedStatement stmt = null;
try {
Class.forName(sDBDriver);
conn = DriverManager.getConnection(sConnStr, "scott", "scott");
String sqlstr = "SELECT procinst.dbid_,actinst.activity_name_,actinst.start_,actinst.end_,"
+ " actinst.htask_,histtask.assignee_"
+ " FROM JBPM4_HIST_ACTINST actinst,"
+ " JBPM4_HIST_PROCINST procinst,"
+ " JBPM4_HIST_TASK histtask"
+ " WHERE actinst.hproci_=procinst.dbid_"
+ " AND histtask.dbid_=actinst.htask_"
+ " AND procinst.dbid_=("
+ " SELECT task.procinst_"
+ " FROM JBPM4_TASK task"
+ " INNER JOIN JBPM4_HIST_TASK hist ON hist.dbid_=task.dbid_"
+ " WHERE hist.dbid_=?"
+ " )"
+ " AND actinst.activity_name_!='exclusive1'"
+ " ORDER BY procinst.dbid_,actinst.dbid_";
stmt = conn.prepareStatement(sqlstr);
stmt.setString(1, taskId);
rs = stmt.executeQuery();
while (rs.next()) {
%>
<tr <%if (rs.getString(4) == null || rs.getString(4) == "") {%>
bgcolor="green" <%}%>>
<td><%=rs.getString(1)%></td>
<td><%=rs.getString(2)%></td>
<td><%=rs.getString(3)%></td>
<td><%=rs.getString(4)%></td>
<td><%=rs.getString(6)%></td>
</tr>
<%
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
}
%>
</tbody>
</table>
</body>
</html>
//submit_manager.jsp
注:String result = request.getParameter("result");先取出了之前有定义的相同name,但value不同的流程result属性,然后taskService.completeTask(taskId, result);这个用来定义流程流转到的下一个节点的节点名。
<%@page contentType="text/html;charset=UTF-8" %>
<%@page import="java.util.*,org.jbpm.api.*"%>
<%@page import="java.sql.*" %>
<%
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
ProcessEngine processEngine = Configuration.getProcessEngine();
TaskService taskService = processEngine.getTaskService();
String username = (String) session.getAttribute("username");
String taskId = request.getParameter("taskId");
String result = request.getParameter("result");
System.err.println(taskId);
//result = new String(result.getBytes("ISO-8859-1"), "UTF-8");
//操作数据库
String sDBDriver = "oracle.jdbc.driver.OracleDriver";
String sConnStr = "jdbc:oracle:thin:@10.142.7.9:1521:orcl";
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try
{
Class.forName(sDBDriver);
conn = DriverManager.getConnection(sConnStr,"scott","scott");
conn.setAutoCommit(false);
//查询待办事项列表
String sql1 = "UPDATE jbpm4_hist_task task set task.assignee_=? where task.dbid_=?";
stmt = conn.prepareStatement(sql1);
stmt.setString(1,username);
stmt.setString(2,taskId);
stmt.executeUpdate();
conn.commit();
}catch(Exception e){
e.printStackTrace();
try{
conn.rollback();
}catch(Exception e1){
e1.printStackTrace();
}
}finally{
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
}
taskService.completeTask(taskId, result);
response.sendRedirect("task-personal-list.jsp");
%>
//操作结果:
我们在此选择“批准”,同时由于申请天数大于3天,将由老板进行下一个审批。
2.9 流程自动决策
“经理审批”提交后,并没有直接提交给老板。如果流程中定义了决策节点(decision),流程虚拟机(pvm)将对其中的定义内容进行判断,判断结束后才将流程流转到相应的节点。
在这个流程中,我们定义了一个决策节点,用于根据天数来判断,“经理审批”通过后将是“老板审批”还是结束流程。流程决策定义如下:
<decision expr="#{day > 3 ? '老板审批' : '结束'}" g="200,308,48,48"
name="exclusive1">
<transition g="-39,-10" name="结束" to="end1" />
<transition g="339,342:-71,-17" name="老板审批" to="老板审批" />
</decision>
注:expr:定义了流程的决策公示,其中语法同el表达了相同
Transition:定义了流程的流转方向。
此流程定义中expr的公式说明如果流程中的day值大于3,下一个流程转向“老板审批”,day值小于3则直接转向“结束”。
根据jbpm4.4api的说明,决策(decision)也可由一个来进行决策,而不用expr简单的表达式来进行决策。具体请参看,jbpm4.4API文档说明。
2.10 流程办理(老板审批)
在之前的流程定义点,我们进行如下定义:
<task form="boss.jsp" g="294,375,92,52" candidate-groups="boss"
name="老板审批">
<transition g="339,457:" to="end1" />
</task>
注:form:处理老板审批的处理页面
Candidate-groups:处理当前任务节点的用户组,如果不定义则任务都在这里节点进行 操作,还可以像“申请”节点一样,定义assignee的属性,但是candidate-groups 和assigness同时只能使用其中的一个。
这里定义了两个transition,分别是“批准”和“驳回”,决定的流程的下一个流转节点 是哪里。在具体操作中,将详细说明。
//boss.jsp源码
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@page import="org.jbpm.api.*,org.jbpm.api.task.*"%>
<%@page import="java.sql.*"%>
<%
ProcessEngine processEngine = Configuration.getProcessEngine();
TaskService taskService = processEngine.getTaskService();
String taskId = request.getParameter("id");
Task task = taskService.getTask(taskId);
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<fieldset><legend>老板审核</legend>
<form action="submit_boss.jsp" method="post"><input type="hidden"
name="taskId" value="${param.id}">申请人:<%=taskService.getVariable(taskId, "owner")%><br />
请假时间:<%=taskService.getVariable(taskId, "day")%><br />
请假原因:<%=taskService.getVariable(taskId, "reason")%><br />
<input type="submit" value="完成" /></form>
</fieldset>
<br />
<table border="1" width="100%">
<caption>当前实例办理情况</caption>
<thead>
<tr>
<td>流程ID</td>
<td>节点名称</td>
<td>开始时间</td>
<td>结束时间</td>
<td>参与人员</td>
</tr>
</thead>
<tbody>
<%
String sDBDriver = "oracle.jdbc.driver.OracleDriver";
String sConnStr = "jdbc:oracle:thin:@10.142.7.9:1521:orcl";
Connection conn = null;
ResultSet rs = null;
PreparedStatement stmt = null;
try {
Class.forName(sDBDriver);
conn = DriverManager.getConnection(sConnStr, "scott", "scott");
String sqlstr = "SELECT procinst.dbid_,actinst.activity_name_,actinst.start_,actinst.end_,"
+ " actinst.htask_,histtask.assignee_"
+ " FROM JBPM4_HIST_ACTINST actinst,"
+ " JBPM4_HIST_PROCINST procinst,"
+ " JBPM4_HIST_TASK histtask"
+ " WHERE actinst.hproci_=procinst.dbid_"
+ " AND histtask.dbid_=actinst.htask_"
+ " AND procinst.dbid_=("
+ " SELECT task.procinst_"
+ " FROM JBPM4_TASK task"
+ " INNER JOIN JBPM4_HIST_TASK hist ON hist.dbid_=task.dbid_"
+ " WHERE hist.dbid_=?"
+ " )"
+ " AND actinst.activity_name_!='exclusive1'"
+ " ORDER BY procinst.dbid_,actinst.dbid_";
System.out.println(sqlstr);
stmt = conn.prepareStatement(sqlstr);
stmt.setString(1, taskId);
rs = stmt.executeQuery();
while (rs.next()) {
%>
<tr <%if (rs.getString(4) == null || rs.getString(4) == "") {%>
bgcolor="green" <%}%>>
<td><%=rs.getString(1)%></td>
<td><%=rs.getString(2)%></td>
<td><%=rs.getString(3)%></td>
<td><%=rs.getString(4)%></td>
<td><%=rs.getString(6)%></td>
</tr>
<%
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
}
%>
</tbody>
</table>
</body>
</html>
//submit_boss.jsp
注:由于老板审批之后的流程出口只有一个,所以在完成当前节点任务时,除了任务ID外,不需要任何参数。
<%@page import="java.util.*,org.jbpm.api.*"%>
<%@page import="java.sql.*" %>
<%
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
ProcessEngine processEngine = Configuration.getProcessEngine();
TaskService taskService = processEngine.getTaskService();
String taskId = request.getParameter("taskId");
String username = (String) session.getAttribute("username");
//操作数据库
String sDBDriver = "oracle.jdbc.driver.OracleDriver";
String sConnStr = "jdbc:oracle:thin:@10.142.7.9:1521:orcl";
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
//String sql1 = "UPDATE jbpm4_hist_task task set task.assignee_=? where task.dbid_=?";
//out.println(sql1);
//out.println(username);
//out.println(taskId);
try
{
Class.forName(sDBDriver);
conn = DriverManager.getConnection(sConnStr,"scott","scott");
conn.setAutoCommit(false);
//查询待办事项列表
String sql1 = "UPDATE jbpm4_hist_task task set task.assignee_=? where task.dbid_=?";
stmt = conn.prepareStatement(sql1);
stmt.setString(1,username);
stmt.setString(2,taskId);
stmt.executeUpdate();
conn.commit();
}catch(Exception e){
e.printStackTrace();
try{
conn.rollback();
}catch(Exception e1){
e1.printStackTrace();
}
}finally{
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
}
taskService.completeTask(taskId);
response.sendRedirect("task-personal-list.jsp");
%>
//操作说明
点击“完成”,将结束流程流转。
这样就完成了一个简单流程的操作。
3、 数据库表简要说明
jBPM4.4的数据库表分成以下几类:
1)和系统相关:
这个只有JBPM4_PROPERTY
2)和ProcessDefinition相关的表:
有:JBPM4_DEPLOYMENT、JBPM4_DEPLOYPROP、JBPM4_LOB
3)和开启一个instance相关:
有JBPM4_EXECUTION、JBPM4_TASK、JBPM4_JOB、JBPM4_VARIABLE、JBPM4_SWIMLANE、 JBPM_PARTICIPATION
4)和历史相关的表:
JBPM4_HIS_ACTINST、JBPM4_HIS_DETAIL、JBPM4_HIS_PROCINST、JBPM4_HIS_TASK、JBPM4_HIS_VAR
5)和用户/组相关的表有:
JBPM4_ID_USER、JBPM4_ID_GROUP、JBPM4_ID_MEMBERSHIP
具体数据库的详细数据结果可以参看,随文所附的jbpm.pdm文件,使用powerdesigner打开。
integration spring3
1:将jbpm.cfg.xml里面的
<import resource="jbpm.tx.hibernate.cfg.xml" />
换成
<import resource="jbpm.tx.spring.cfg.xml" />
2: 根据实际情况配置jbpm.hibernate.cfg.xml
主要是几个MAPPING文件
<mapping resource="jbpm.repository.hbm.xml" /> <mapping resource="jbpm.execution.hbm.xml" /> <mapping resource="jbpm.history.hbm.xml" /> <mapping resource="jbpm.task.hbm.xml" /> <mapping resource="jbpm.identity.hbm.xml" />
3:配置spring的application-context.xml
<bean id="springHelper" class="org.jbpm.pvm.internal.processengine.SpringHelper" > </bean> <bean id="processEngine" factory-bean="springHelper" factory-method="createProcessEngine" />
4:配置其他如 sessionfactory 和 事务管理器
bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource"> <ref bean="dataSource" /> </property> <property name="configLocations"> <list> <value>classpath:/jbpm.hibernate.cfg.xml</value> </list> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> <property name="dataSource" ref="dataSource" /> </bean>
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();
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();
%>
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>
<%}%>
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>
<%}%>
相关推荐
JBPM-4.4演示请假流程步骤和源代码 步骤: 1.首先用MyProcessDesigner_流程设计器设计一个‘请假流程图’,保存成zip根式(属性别忘记写) 2.启动tomcat:startup.bat 3.浏览器访问地址:...
【Jbpm工作流示例】是一个以Jbpm 4.4版本为基础的工作流程管理系统实例,主要用于展示和学习工作流的基本操作和功能。Jbpm(Java Business Process Management)是一个开源的工作流引擎,它提供了全面的业务流程管理...
5. **MyJbpm4-Console**:这个文件名可能是项目中的一个模块或者子目录,可能包含了与JBPM相关的管理控制台,如流程定义的查看、部署、监控等功能。用户可以通过这个控制台来交互式地管理和操作流程。 综合上述技术...
内容概要:本文介绍了如何利用CST软件进行三维超材料的能带计算。首先概述了三维超材料的独特性质及其广泛应用前景,接着简要介绍了CST软件的功能特点。随后详细阐述了能带计算的具体步骤,包括模型建立、材料参数设置、网格划分与求解设置以及最终的计算与结果分析。最后给出了一段Python代码示例,展示了如何处理CST输出的数据并绘制能带图。文章强调了计算机模拟技术对于深入了解超材料电子结构和物理性质的重要性。 适合人群:从事材料科学研究的专业人士,尤其是对三维超材料和电磁场模拟感兴趣的科研工作者和技术人员。 使用场景及目标:适用于希望借助CST软件开展三维超材料能带计算的研究项目,旨在提高对超材料的理解,推动相关领域的技术创新和发展。 其他说明:文中提供的Python代码仅为示例,在实际操作时可根据具体情况进行调整优化。同时,掌握CST软件的基本操作和电磁理论基础知识有助于更好地理解和应用本文内容。
内容概要:本文详细介绍了基于FPGA的永磁同步伺服系统的矢量控制设计,涵盖了从电流环到速度环的关键模块实现。具体包括Clarke和Park变换、PI调节器、AD7606采样、正交编码器反馈以及SVPWM生成等部分。文中提供了详细的Verilog代码片段,展示了各个模块的具体实现方法和技术细节。特别强调了定点数处理、时序设计和跨时钟域处理等方面的技术挑战及其解决方案。 适合人群:具备一定FPGA开发经验和电机控制基础知识的研发人员。 使用场景及目标:适用于希望深入了解FPGA在电机控制应用中的具体实现方式,特别是矢量控制和电流环设计的专业人士。目标是掌握FPGA平台下高效、低延迟的电机控制系统设计方法。 阅读建议:由于涉及大量具体的Verilog代码和硬件设计细节,建议读者在阅读过程中结合实际项目进行实验和调试,以便更好地理解和掌握相关技术。
经典飞机大战游戏是理解实时交互系统设计的绝佳载体。本文将深入剖析现代空战游戏的核心模块,涵盖刚体运动学、弹道轨迹优化、碰撞检测算法等关键技术,揭示二维游戏背后复杂的三维数学建模过程。
scratch少儿编程逻辑思维游戏源码-冰塔.zip
scratch少儿编程逻辑思维游戏源码-弹跳(4).zip
内容概要:本文详细介绍了COMSOL软件中三种常见的焊接热源模型——双椭球热源、高斯旋转体热源和柱状体热源。双椭球热源适用于模拟移动热源(如激光焊、电弧焊),通过调整轴向系数a1和a2来控制热流分布;高斯旋转体热源适合小范围焊接,采用三维高斯函数描述热流密度;柱状体热源则用于深熔焊场景,特点是计算速度快。文中还提供了每种模型的具体代码实现,并强调了调试时需要注意的关键点,如时间步长、网格加密等。此外,作者分享了一些实用技巧,如将热源参数设置为全局变量并利用参数扫描功能提高调试效率。 适合人群:从事焊接工艺仿真、材料加工领域的研究人员和技术人员,以及对COMSOL建模感兴趣的工程技术人员。 使用场景及目标:帮助用户选择合适的热源模型进行焊接模拟,确保模拟结果的准确性;提供具体的代码实现和调试方法,使用户能够快速掌握并应用于实际项目中。 其他说明:文中提到的热源模型不仅限于理论介绍,还包括实际操作中的注意事项和优化建议,有助于提升模拟效果和工作效率。
内容概要:本文介绍了将基于RBF神经网络的PID控制器应用于永磁同步电机(PMSM)转速环控制的方法及其性能优势。传统的PID控制器在面对非线性和时变系统时存在参数整定困难的问题,而引入RBF神经网络可以实现实时在线调参,提高系统的灵活性和鲁棒性。文中详细描述了Simulink模型的设计,特别是Matlab s-function模块中RBF神经网络的具体实现,包括高斯函数激活和带惯性的权值更新机制。实验结果显示,在转速突变情况下,改进后的控制器能够迅速稳定系统,超调量控制在2%以内,调节时间较传统方法缩短约40%,并且在负载变化时表现出色,无需重新整定参数。 适合人群:从事电机控制系统研究和开发的技术人员,尤其是对PID控制器优化感兴趣的工程师。 使用场景及目标:适用于需要提升PMSM转速环控制精度和响应速度的应用场合,如工业自动化设备、机器人等领域。目标是通过引入智能算法解决传统PID控制器参数整定难题,提高系统性能。 阅读建议:关注RBF神经网络与PID控制器结合的具体实现细节,特别是在Matlab s-function模块中的编码技巧以及参数调整策略。同时,注意学习率的选择和动量项的作用,这对于实际应用至关重要。
scratch少儿编程逻辑思维游戏源码-GTA 6.zip
scratch少儿编程逻辑思维游戏源码-仓鼠跑酷.zip
scratch少儿编程逻辑思维游戏源码-超级麦克世界.zip
scratch少儿编程逻辑思维游戏源码-400年.zip
少儿编程scratch项目源代码文件案例素材-气球足球.zip
少儿编程scratch项目源代码文件案例素材-沙漠迷城.zip
scratch少儿编程逻辑思维游戏源码-比谁高.zip
少儿编程scratch项目源代码文件案例素材-乾坤大挪移.zip
scratch少儿编程逻辑思维游戏源码-菜鸟跳跃.zip
内容概要:本文档详细介绍了C++语言的基础知识、高级特性及其应用。首先,文档回顾了C++对C语言的扩展,包括面向对象编程的支持、增强的语法特性(如命名空间、引用、常量处理等)。接着,深入探讨了类和对象的使用,涵盖构造函数、析构函数、拷贝构造函数、深浅拷贝等重要概念。文档还讲解了单例模式的设计与实现、C++面向对象模型的核心要素(如this指针、静态成员、友元函数)、继承与派生的关系及其实现细节、多态性的原理与应用。此外,文档详细介绍了C++的模板机制、类型转换、异常处理机制、输入输出流操作、STL(标准模板库)的容器和算法等内容。每个部分都通过具体的代码示例和解释,帮助读者理解和掌握C++的关键特性和最佳实践。 适合人群:具备一定编程基础,尤其是熟悉C语言的开发者;希望深入了解C++语言特性和面向对象编程思想的程序员;从事C++开发工作的工程师和技术爱好者。 使用场景及目标:①掌握C++语言的核心概念和高级特性;②理解并能够应用面向对象编程的基本原则和模式;③学习如何使用STL容器和算法优化代码性能;④提升C++程序的健壮性和可维护性,特别是在处理复杂数据结构和算法时;⑤掌握异常处理和类型转换的最佳实践,确保程序的稳定性和安全性。 其他说明:本文档不仅提供了理论知识,还结合了大量实例代码,便于读者边学边练。对于每一个知识点,文档都力求做到详尽解释,确保读者能够透彻理解并灵活运用。文档内容全面覆盖了C++编程的各个方面,从基础语法到高级特性,适合不同层次的学习者逐步深入学习。