- 浏览: 171529 次
- 性别:
- 来自: 重庆
文章分类
最新评论
-
chgliu:
谢谢~很实用
很好的vi教程(接合例子) -
pollyduan:
gameboy4913 写道在MsgContetnt前加入TP ...
长短信实现系列之联通SGIP1.2 -
yanxiaoyu:
String srcXML = "...xml文字. ...
dom4j 中selectNodes选取节点排序问题 -
510372845:
学习了。谢谢。
dom4j 中selectNodes选取节点排序问题 -
haohaoxuexi1311:
settings.xml里面的配置,需要在Nexus里做对应的 ...
【原创】maven + nexus 的settings.xml配置实例
使用jBpm支持高级用户交互模式
作者 Boris Lublinsky 译者 胡键 发布于 2010年1月15日 上午12时35分
<script src="http://s7.addthis.com/js/250/addthis_widget.js#username=infoq" type="text/javascript"></script>
<!-- AddThis Button END -->许多通用业务流程都包含人类参与者。人类活动,从简单场景(如人工批准)到复杂场景(涉及复杂的数据输入),在流程实现中引入了新的方面,如人类交互模式。人类交互模式的一个典型集合1 包括:
相关厂商 内容
相关赞助商
2010第五届敏捷中国大会四大主题:业务敏捷、技术实践、组织转型及管理实践,为技术团队搭建开放的交流平台。现在报名,8折优 惠 !Powered by ThoughtWorks
- 四眼原则(The 4-eyes principle) ,通常又被称为“职责分离”,它是决策由多人彼此独立作出时的一个常见场景。在很多情况下,很容易就能得到另一个观点/签名。
- 任命(Nomination) 是指上级根据团队成员的任务安排、工作负荷或经验人工地将任务分配给他的情形。
- 任务通常被建模来表达一种预期:它们将在确定时间段内完成。如果任务没有按预期地进展,就需要一种上报(escalation) 机制。两种典型的上报实现是:重新分配任务,并常常附带一个上报已经发生的通知;或任务未按时完成的通知(通常发给经理)。
- 链状执行(Chained execution) 是一个流程(片断),其中的一系列步骤是由同一个人来完成。
在本文中,我将讨论如何使用JBoss jBPM来实现这些高级的交互模式。
jBPM中的任务管理
jBPM的一个核心功能2 是为人类管理任务和任务列表。jBPM允许将任务和任务节点作为整个流程设计的一部分使用。
任务一般在jBPM中定义成任务节点。单个任务节点可以包含一个或多个任务。包含任务节点的jBPM流程的一个公共行为就是等待任务节点中的全部任务完成,然后继续执行。某个任务可被分配3 给个人、用户组或泳道:
- 假如任务被分配给某个特定用户,那么就只有这个使用者可以执行它。
- 假如任务被分配给某个用户组,那么这个组内的任何参与者都能执行这个任务。jBPM使用的是参与者池(pooled actors)符号(它可以包含组名、组名列表和参与者个人列表等),而不是组ID。如果用户开始执行在他们组任务列表中的任务,最终可能会引起冲突4 ——可能有多人开始执行相同的任务。为了避免这种情况,在开始执行任务之前,用户应该将任务从组任务列表移动到他们自己的任务列表中。
- 泳道代表一个流程角色,它通常被分配给一个用户组。它是一种指定流程中的多个任务要由同一参与者完成的机制5 。因此,在第一个任务被分配给某个泳道之后,流程就会记住所有在相同泳道内的后续任务都将由同一参与者完成。
jBPM提供了两种定义任务分配的基本方法:作为流程定义的一部分或通过编程实现。如果是作为流程定义的一部分,分配可以通过指定具体用户、用户组 或泳道 完成。此外,可以使用表达式根据流程变量动态确定某个具体用户。完整的编程实现是基于分配处理器(assignment handler)的6 ,它允许任务根据任意的计算规则去查找用户ID。
流程定义描述流程实例的方式类似任务描述任务实例的方式。当流程执行时,一个流程实例——流程的运行时表示——就会被创建。类似,一个任务实例——任务的运行时表示——就会被创建。根据任务定义,任务实例被分配给一个参与者/参与者组。
任务实例的一个作用就是支持用户交互——把数据显示给用户并从用户那里收集数据。一个jBPM任务实例拥有访问流程(令牌)变量7 的全部权限,而且还可以有自己的变量。任务能够拥有自己的变量对于以下场景非常有用:
- 在任务实例中创建流程变量的副本,这样对任务实例变量的即时更新只有在该任务完成且这些副本被提交给流程变量时才会影响流程变量。
- 创建更好支持用户活动的“派生(计算)”变量。
任务自己的变量在jBPM中是通过任务控制器处理器(task controller handler)支持的8 ,它可以在任务实例创建时生成任务实例数据(从流程数据),并在任务实例完成时将任务实例数据提交给流程变量。
实现四眼原则
我们上面已经说过,实现四眼原则意味着要允许多人同时干一个活。它的实现有以下几种可能方法:
- 在任务外解决:需要大量时间的任务并行循环(parallel looping)9 。
- 使用动作处理器(Action handler):附加到任务节点的进入事件(enter event),基于流程实例变量创建多个节点实例10 。
- 在任务内解决:引入“任务接受量(task take)”(类似jPDL 4)并允许某个任务实例可被接受多次。
根据jBPM最佳实践11 ——“扩展jBPM API而不是去搞复杂的流程建模”12 ,我决定采用任务内解决的方法。这就要求修改jBPM提供的任务和任务实例类。
扩展Task类
jBPM任务的定义被包含在org.jbpm.taskmgmt.def.Task类中。为了支持四眼原则,我们需要给类增加以下的字段/方法(清单1):
protected int numSignatures = 1; public int getNumSignatures(){ return numSignatures ; } public void setNumSignatures(int numSignatures){ this .numSignatures = numSignatures ; }
清单1 给Task类增加字段和方法
这个新的参数允许指定任务完成所需的任务处理人数量。缺省值为1,这意味着,只有1个用户应该/可以处理这个任务。
jBPM使用Hibernate来向数据库保存和读取数据。为了让我们新加的变量持久化,我们需要更新Task类的Hibernate配置文件(Task.hbm.xml),它在org.jbpm.taskmgmt.def文件夹中,增加代码如下(清单2)
<property name ="numSignatures " column ="NUMSIGNATURES_ " />
清单2 在Task映射文件中指定新增域
为了让我们新加的属性能被流程定义和数据库正确读取,我们需要修改org.jbpm.jpdl.xml.JpdlXmlReader类以正确地读取我们的新属性(清单3)
String numSignatureText = taskElement.attributeValue("numSignatures" ); if (numSignatureText != null ) { try { task.setNumSignatures(Integer.parseInt(numSignatureText)); } catch (Exception e){} }
清单3 读取numSignature属性
最后,因为JpdlXmlReader根据模式来验证XML,因此我们需要在jpdl-3.2.xsd中增加一个属性定义(清单4):
<xs:element name ="task" > …………………. <xs:attribute name ="numSignatures " type ="xs:string " />
清单4 在jpdl-3.2.xsd中增加numSignatures属性
当完成这些工作,任务定义就被扩展可以使用numSignatures属性(清单5):
<task name
="task2
" numSignatures
= "2
">
<assignment pooled-actors
="Peter, John
"></assignment>
</task>
清单5 给任务定义增加numSignatures属性
扩展TaskInstance类
在扩展完任务类后,我们还需要创建一个自定义的任务实例类来跟踪分配给该任务实例13 的参与者,并确保所有被分配的参与者完成类执行(清单6)。
package com.navteq.jbpm.extensions; import java.util.Date; import java.util.LinkedList; import java.util.List; import org.jbpm.JbpmException; import org.jbpm.taskmgmt.exe.TaskInstance; public class AssignableTaskInstance extends TaskInstance { private static final long serialVersionUID = 1L; private List<Assignee> assignees = new LinkedList<Assignee>(); private String getAssigneeIDs(){ StringBuffer sb = new StringBuffer(); boolean first = true ; for (Assignee a : assignees ){ if (!first) sb.append(" " ); else first = false ; sb.append(a.getUserID()); } return sb.toString(); } public List<Assignee> getAssignees() { return assignees; } public void reserve(String userID) throws JbpmException{ if (task == null) throw new JbpmException("can't reserve instance with no task" ); // Duplicate assignment is ok for (Assignee a : assignees ){ if (userID.equals(a.getUserID())) return ; } // Can we add one more guy? if (task .getNumSignatures() > assignees .size()){ assignees .add(new Assignee(userID)); return ; } throw new JbpmException("task is already reserved by " + getAssigneeIDs()); } public void unreserve(String userID){ for (Assignee a : assignees ){ if (userID.equals(a.getUserID())){ assignees .remove(a); return ; } } } private void completeTask(Assignee assignee, String transition){ assignee.setEndDate(new Date()); // Calculate completed assignments int completed = 0; for (Assignee a : assignees ){ if (a.getEndDate() != null) completed ++; } if (completed < task .getNumSignatures()) return ; if (transition == null) end(); else end(transition); } public void complete(String userID, String transition) throws JbpmException{ if (task == null) throw new JbpmException("can't complete instance with no task" ); // make sure it was reserved for (Assignee a : assignees ){ if (userID.equals(a.getUserID())){ completeTask(a, transition); return ; } } throw new JbpmException("task was not reserved by " + userID); } public boolean isCompleted(){ return (end != null ); } }
清单6 扩展TaskInstance类
这个实现扩展了jBPM提供的TaskInstance类,并跟踪完成该实例所需的参与者个数。它引入了几个新方法,允许参与者预留(reserve)/退还(unreserve)任务实例,以及让指定参与者完成任务执行。
清单6的实现依赖一个支持类Assignee(清单7)
package com.navteq.jbpm.extensions; import java.io.Serializable; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; public class Assignee implements Serializable{ private static final long serialVersionUID = 1L; private static final DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); long id = 0; protected String startDate = null; protected String userID = null; protected String endDate = null; public Assignee(){} public Assignee(String uID){ userID = uID; startDate = dateFormat.format(new Date()); } ////////////Setters and Getters /////////////////// public long getId() { return id; } public void setId(long id) { this.id = id; } public String getStartDate() { return startDate; } public void setStartDate(String startDate) { this.startDate = startDate; } public String getUserID() { return userID; } public void setUserID(String id) { userID = id; } public String getEndDate() { return endDate; } public void setEndDate(String endDate) { this.endDate = endDate; } public void setEndDate(Date endDate) { this.endDate = dateFormat.format(endDate); } public void setEndDate() { this.endDate = dateFormat.format(new Date()); } public String toString(){ StringBuffer bf = new StringBuffer(); bf.append(" Assigned to "); bf.append(userID); bf.append(" at "); bf.append(startDate); bf.append(" completed at "); bf.append(endDate); return bf.toString(); } }
清单7 Assignee类
自定义的TaskInstance类和Assignee类都必须保存到数据库中。这意味着需要给这两个类实现Hibernate映射14 (清单8,9):
<?xml version
="1.0
"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping auto-import
="false
" default-access
="field
">
<subclass name
name="com.navteq.jbpm.extensions.AssignableTaskInstance
"
extends
="org.jbpm.taskmgmt.exe.TaskInstance
"
discriminator-value
="A">
<list name
="assignees
" cascade
="all
" >
<key column
="TASKINSTANCE_
" />
<index column
="TASKINSTANCEINDEX_
"/>
<one-to-many class
="com.navteq.jbpm.extensions.Assignee
" />
</list>
</subclass>
</hibernate-mapping>
清单8 自定义任务实例的Hibernate映射文件
<?xml version
="1.0
"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping auto-import
="false
" default-access
="field
">
<class name
="com.navteq.jbpm.extensions.Assignee
"
table
="JBPM_ASSIGNEE">
<cache usage
="nonstrict-read-write
"/>
<id name
="id
" column
="ID_
"><generator class="native
" /></id>
<!-- Content -->
<property name
="startDate
" column
="STARTDATE_"
/>
<property name
="userID
" column
="USERID_"
/>
<property name
="endDate
" column
="ENDDATE_"
/>
</class>
</hibernate-mapping>
清单9 Assignee类的Hibernate映射文件
要让jBPM能够使用我们的自定义任务实例实现,我们还需要提供一个自定义的任务实例工厂(清单10)。
package com.navteq.jbpm.extensions; import org.jbpm.graph.exe.ExecutionContext; import org.jbpm.taskmgmt.TaskInstanceFactory; import org.jbpm.taskmgmt.exe.TaskInstance; public class AssignableTaskInstanceFactory implements TaskInstanceFactory { private static final long serialVersionUID = 1L; @Override public TaskInstance createTaskInstance(ExecutionContext executionContext) { return new AssignableTaskInstance(); } }
清单10 自定义的任务实例工厂
最后,为了让jBPM运行时使用正确的任务实例工厂(清单10),还必须创建一个新的jBPM配置(清单11)。
<jbpm-configuration>
<bean name
="jbpm.task.instance.factory"
class
="com.navteq.jbpm.extensions.AssignableTaskInstanceFactory
" singleton
="true
"
/>
</jbpm-configuration>
清单11 jBPM配置
完成所有这些变更之后(清单1-11),一个典型的任务处理显示如下:
List<String> actorIds = new LinkedList<String>(); actorIds.add("Peter"); List<TaskInstance> cTasks = jbpmContext.getGroupTaskList(actorIds) TaskInstance cTask = cTasks.get(0); AssignableTaskInstance aTask = (AssignableTaskInstance)cTask; try{ aTask.reserve("Peter"); // Save jbpmContext.close(); } catch(Exception e){ System.out.println("Task " + cTask.getName() + " is already reserved"); e.printStackTrace(); }
清单12 处理可分配任务实例
这里,在得到某个用户的任务实例并将其转变成可分配任务实例之后,我们将试着预留它15 。一旦预留成功,我们将关闭jBPM运行时以提交事务。
实现任命
JBoss jBPM可以非常轻易的实现手动将任务分配给特定用户。根据jBPM提供的简单API,可以完成将任务实例从一个任务列表移动到另一个任务列表,因此给某个用户分配任务相当直接(清单13)
List<String> actorIds = new LinkedList<String>(); actorIds.add("admins "); String actorID = "admin "; List<TaskInstance> cTasks = jbpmContext.getGroupTaskList(actorIds) ; TaskInstance cTask = cTasks.get(0); cTask.setPooledActors((Set )null); cTask.setActorId(actorID);
清单13 将任务重新分配给指定用户
jBPM提供了2类不同的API来设置参与者池:一类接收字符串id数组,另一类则接收id集合。如果要清空一个池,就要使用那个接收集合的API(传入一个null集合)。
实现上报
前面已经说过,上报一般被实现为任务的重新分配,并常常附带一个上报已发生的通知;或是实现成一个任务未及时完成的通知。
实现为重新分配的上报
尽管jBPM不直接支持上报,但它提供了2个基本的机制:超时和重新分配(参见上节)。粗一看,实现上报只需将这二者结合即可,但是仔细一想还是存在一些困难:
- jBPM实现中的关系并不总是双向的。如,从一个任务节点我们可以找到所有这个节点定义的任务,但是从一个任务,并没有API可以完成找到包含它的任务节点的工作16 ;由某个任务实例,你可以得到一个任务,但是没有由某个任务得到所有实例的API,诸如此类。
- 超时不是发生在任务自身,而是发生在任务节点上。由于某个节点可以关联多个任务,并且jBPM关系实现并不是双向的(见上),因此要跟踪当前任务实例就需要其他的支持手段。
以重新分配实现的上报的整个实现17 涉及3个处理器:
- 负责给任务分配参与者的分配处理器。这个处理器跟踪它是一个首次任务调用还是一个上报任务调用。清单14给出了一个分配处理器的例子。
package com.sample.action; import org.jbpm.graph.def.Node; import org.jbpm.graph.exe.ExecutionContext; import org.jbpm.taskmgmt.def.AssignmentHandler; import org.jbpm.taskmgmt.exe.Assignable; public class EscalationAssignmentHandler implements AssignmentHandler { private static final long serialVersionUID = 1L; @Override public void assign(Assignable assignable, ExecutionContext context) throws Exception { Node task = context.getToken().getNode(); if(task != null ){ String tName = task.getName(); String vName = tName + "escLevel "; Long escLevel = (Long)context.getVariable(vName); if(escLevel == null ){ // First time through assignable.setActorId("admin "); } else { // Escalate assignable.setActorId("bob "); } } } }
清单14 分配处理器示例
这里我们尝试得到一个包含了给定任务上报次数的流程变量。如果变量未定义,则就分配“admin”为任务拥有者,否则任务就被分配给“bob”。在这个处理器中可以使用任何其他的分配策略。
- 任务实例创建动作处理器(清单15),它保存流程实例上下文的任务实例id
package com.sample.action; import org.jbpm.graph.def.ActionHandler; import org.jbpm.graph.def.Node; import org.jbpm.graph.exe.ExecutionContext; import org.jbpm.taskmgmt.exe.TaskInstance; public class TaskCreationActionHandler implements ActionHandler { private static final long serialVersionUID = 1L; @Override public void execute(ExecutionContext context) throws Exception { Node task = context.getToken().getNode(); TaskInstance current = context.getTaskInstance(); if ((task == null ) || (current == null )) return ; String tName = task.getName(); String iName = tName + "instance "; context.setVariable(iName, new Long(current.getId())); } }
清单15 任务实例创建处理器
- 任务节点计时器触发调用的超时处理器(清单16)。
package com.sample.action; import org.jbpm.graph.def.ActionHandler; import org.jbpm.graph.def.GraphElement; import org.jbpm.graph.exe.ExecutionContext; import org.jbpm.taskmgmt.exe.TaskInstance; public class EscalationActionHandler implements ActionHandler { private static final long serialVersionUID = 1L; private String escalation; @Override public void execute(ExecutionContext context) throws Exception { GraphElement task = context.getTimer().getGraphElement(); if (task == null ) return ; String tName = task.getName(); String vName = tName + "escLevel "; long escLevel = (long )context.getVariable(vName); if (escLevel == null ) escLevel = new long (1); else escLevel += 1; context.setVariable(vName, escLevel); String iName = tName + "instance "; long taskInstanceId = (long )context.getVariable(iName); TaskInstance current = context.getJbpmContext().getTaskInstance(taskInstanceId); if (current != null ){ current.end(escalation); } } }
清单16 超时处理器
这个处理器首先记录上报计数器,接着完成此节点关联的任务实例。任务实例的完成伴随有一个变迁(一般是回到任务节点)。
使用以上描述的处理器实现上报的简单流程例子显示在清单17中。
<?xml version="1.0
" encoding
="UTF-8
"?>
<process-definition
xmlns
="urn:jbpm.org:jpdl-3.2
"
name
="escalationHumanTaskTest
">
<start-state name
="start">
<transition to="customTask
"></transition>
</start-state>
<task-node name
="customTask
">
<task name
="task2
">
<assignment class
="com.sample.action.EscalationAssignmentHandler
"><
/assignment>
</task>
<event type
="task-create
">
<action name
="Instance Tracking
" class
="com.sample.action.TaskCreationActionHandler
"></action>
</event>
<timer duedate
="10 second
" name
="Escalation timeout
">
<action class
="com.sample.action.EscalationActionHandler
">
<escalation>
escalation
</escalation>
</action>
</timer>
<transition to
="end
" name
="to end
"></transition>
<transition to
="customTask
" name
="escalation
"></transition>
</task-node>
<end-state name
="end
"></end-state>
</process-definition>
清单17 简单流程的上报
实现成通知的上报
jBPM为邮件传递提供了强大支持18 ,这使得实现成通知的上报变得极其简单。邮件传递可由给节点附加定时器,然后触发,它使用已经写好的邮件动作来完成通知传递。
实现链状执行
链状执行直接由jBPM泳道支持,并不需要额外的开发。
总结
不管我们在自动化方面投入多少努力,面对复杂的业务流程,总免不了要有人工介入的可能。在这篇文章中,我给出了一系列已建立的高级人工交互模式,并展示了用jBPM完成它是多么轻而易举。
1 WS-BPEL Extension for People - BPEL4People
2 本文依据jBPM 3完成。jBPM 4对任务支持引入了一些扩展。
3 任务定义规定了分配定义。实际的分配发生在任务实例创建的时候。参见以下的任务实例。
4 这一问题在jBPM 4 中有所缓解,它引入了任务完成所需的任务接受API。
5 参见上面的链状执行模式。
8 不象流程变量,jBPM任务控制器处理器变量是保存在内存中的。因此,如果必须允许多次访问一个支持保存中间执行结果的任务实例,使用任务控制器处理器可能是个错误的解决方案。
9 参见InfoQ文章 for implementation of parallel loop.
10 这要求给任务节点增加create-tasks="false"属性。
12 严格说来,使用任务处理器方法将可能实现我的需求且代码更少,但是这样它就与上报实现(见下)不兼容。此外,我还想展示如何修改缺省的任务和任务实例实现。
13 类似jPDL 4实现。
14 在给自定义任务实例创建Hibernate映射时,将映射实现成缺省类接口的子类要简单得多。现有jBPM Hibernate映射严重依赖任务实例映射,因此从标准实现派生将使引入新任务实例的改动最小。
15 冲突条件仍然可能存在,但是已经最小了。
16 这可以使用Hibernate Queries完成,但没有可供直接使用的API。
17 这个实现假定某个任务节点只包含一个任务,该任务只创建一个任务实例。
发表评论
-
spring和jbpm事务整合问题
2010-09-14 00:33 1428标题:spring和jbpm事 ... -
基于jBPM4的临时动态性需求研究
2010-09-14 00:28 1629基于jBPM4的临时动态性需求研究 http://www.c ... -
JBPM使用的相关问题级解决(一):java.lang.LinkageError 收藏
2010-09-12 22:40 1027JBPM使用的相关问题级解决(一):java.lang.Li ... -
JBPM4 入门系列
2010-09-11 22:35 1780JBPM4初探---HelloWorld 将JBPM4 ... -
jBPM4的PVM解析
2010-08-26 18:39 1391jBPM4的PVM解析 转载:http://www.fam ... -
JBPM 创始人的新项目——Activiti
2010-08-24 18:27 1505Alfresco公司近期宣布了一个新的开源项目——Acti ... -
jbpm4.3轻松 实现会签完整版
2010-08-24 11:59 2334转自:http://hi.baidu.com/ ... -
图解工作流模式和实例
2010-08-24 10:54 2166转载:http://tech.it168.com/ ... -
JBPM工作流引擎内核设计思想及构架
2010-08-24 10:47 1113转载:http://dev.firnow.com/ ... -
工作流参与者和工作项模式分析
2010-08-24 10:41 905工作流参与者和工作项模式分析 收藏 ...
相关推荐
jBPM 4.4 用户指南 jBPM 4.4 是一个功能强大且灵活的工作流引擎,可以运行在独立的服务器上或者嵌入任何...通过阅读该用户指南,用户可以快速了解 jBPM 的安装、配置、使用和开发,并掌握 jBPM 的基础知识和高级技术。
根据提供的文件信息,...通过以上介绍可以看出,《jBPM4.1中文用户手册》不仅涵盖了jBPM的核心功能和使用方法,还提供了大量的示例和最佳实践,对于希望深入了解并应用jBPM的开发人员来说是一份非常有价值的参考资料。
【jBPM4.4开发指南yanghw版】是一份详细指导如何在IT环境中使用jBPM 4.4版本进行流程开发的文档。jBPM(Java Business Process Management)是一个开源的工作流和业务流程管理(BPM)系统,它允许开发者通过图形化的...
- **使用**:介绍了如何在Spring环境中使用JBPM4.3的各种功能。 - **测试**:提供了测试Spring集成环境的方法和技巧。 #### 附录:修改日志 - **修改日志**:记录了版本迭代过程中所做的更改和优化,对于跟踪变更...
jbpm-7.18.0.Final-examples.zip 是一个包含 jBPM 7 最新版本示例的压缩包,旨在帮助开发者更好地理解和学习如何使用 jBPM 进行业务流程管理(Business Process Management)的开发。jBPM 是一个开源的工作流和业务...
- **高级邮件支持**:介绍如何通过jBPM发送邮件通知及相关配置。 - **软件日志**:说明如何配置日志记录以跟踪流程执行过程。 - **历史记录**:探讨如何保存流程的历史记录以便于审计和监控。 - **JBoss集成**:讲解...
- **Facade模式**:为子系统中的一组接口提供一个统一的高级接口,使子系统更容易被使用。 - **Delegate模式**:通过代理对象将请求转发给真实的对象处理,从而实现了职责的分离,提高了系统的可维护性和可扩展性。 ...
它主要负责处理用户请求,协调视图和模型之间的交互。Struts通过Action类处理HTTP请求,并将结果传递给JSP页面进行展示。此外,它还提供了强大的国际化支持、数据验证和文件上传等功能。 **Hibernate** 是一个对象...
通过使用Jbpm、Struts配置校验、泛型技术和POI,他提升了系统的效率和用户体验。这个项目成功解决了公司的实际问题,得到了公司的好评。 总体来看,王强是一位全面的JavaEE软件工程师,他的技能涵盖了软件开发的多...
- **对话管理(Conversation Management)**:对话管理提供了对应用会话的高级支持,使得开发人员能够更容易地管理用户的交互过程。 #### 四、Seam 2.1 实战案例分析 ##### 1. 注册示例 此示例主要展示了如何使用...
- 在另一个未具体描述的项目中,同样涉及了Spring、Hibernate和Struts2的使用,以及Jbpm、easyui、poi和amFlash的集成。 通过以上内容,我们可以看出这位JavaEE软件工程师具备全面的技术栈,涵盖从后端开发到前端...
- **使用“拉”风格的MVC**:解释了如何通过Seam实现客户端发起请求、服务器响应数据的经典MVC模式。 - **可收藏的搜索结果页面**:探讨了如何设计可分享的URL,使得用户可以将搜索结果发送给他人。 - **使用“推...
- **推模式MVC**:讨论了如何在RESTful应用中使用Seam的“推送”模式实现更复杂的MVC交互。 ##### 2.10 在JBoss上运行Seam示例 - **使用JSF 1.2 RI**:提供了如何在JBoss平台上使用JSF 1.2参考实现来部署和运行...
jQuery插件如zTree、validate和jQuery UI则增强了用户界面的功能和交互性。 5. **Java框架**: - 景鑫对Struts 2、Hibernate和Spring有深入的理解和实践。Struts 2提供了强大的MVC框架,而Hibernate是流行的对象...
6. **工作流引擎JBPM**:JBPM是工作流管理系统,课程会介绍流程模板和流程实例的概念,它们之间的关系,JBPM中的流程节点,任务实例,任务分配以及流程定时器的使用。通过实例,学员将了解如何在实际项目中实施工作...
- jBPM用于定义复杂的业务流程,而Seam则负责用户界面的交互逻辑。 ##### 1.8 一个使用Seam工作空间管理的完整范例:IssueTracker - **理解代码**: - 实现了一个基于Seam框架的问题跟踪系统。 - **工作原理**: ...