??xml version="1.0" encoding="utf-8" standalone="yes"?>
Abstract
在默认情况下,jBPM包里有个jbpm.cfg.xml,q个是他的配置文g,我们先看下它的内?
q里,我们再l看?span class="emphasis">jbpm.default.cfg.xml,看下配置文g到底是长啥样.
q个配置文g主要包含?process-engine-context'?'transaction-context'的配|?
首先?span class="emphasis">Context接口,你可以从q里存储,获得对象.它的接口很简?
你看可以从Context中获取到lg,对于IOC容器来说Q一般情况下都会提供一U加载的方式Q比如从xml文gq行加蝲、从资源文gq行加蝲?
我们不妨也看下ConfigurationTest试.
Configuration
cLjBPM的入?你可以从
ConfigurationcM创徏ProcessEngine,而从ProcessEngine中获取到RepositoryService,
ExecutionService, TaskService{等.
Configurationc里有两个实现类,一个是JbpmConfiguration,q是默认的jBPM自带的Configuration解析?
另外一个是SpringConfiguration,q个是jBPM和Spring的集?
在这?我们只看下JbpmConfiguration的实?在JbpmConfigurationc里,我们可以看到他是q么去调用Parser?
解析xml?
在这?我们可以看到,jbpm的配|文件是有两个元素组成的,一个是process-engine-context,另外一个是transaction-context. 其中process-engine-context里面的元素是包括了对外发布的服务,
比如repository-service, execution-service{等. 而transaction-context则包括了他的内部真正实现,比如repository-service对应repository-session.
也可以简单的把repository-servie看做是API, repository-session看做是SPI. q样做的好处?SPI的实?对API一点媄响都没有,很大E度上提供了一个容易集成的Ҏ?
说到q里,应该对jBPM的IOC介绍的差不多?我自己在用JBoss IDM project[2]来实现jBPM的Identity module?需要增加如下的配置.
于是,我需要增加以下的cL完成对象的创建和实例? JbossIdmIdentitySessionFactoryBinding,JbossIdmIdentitySessionFactoryDescriptor,JbossIdmIdentitySessionBinding,
JbossIdmIdentitySessionDescriptor
然后,?span class="emphasis">jbpm.wire.bindings.xml里面注册我们新加的Binding.
从上面我们所说的,不难看出本nq个IOC的实现机制也是很单的,而且也很Ҏ扩展,如果说到时候和Spring, JBoss MC{IOC容器的集成也是很方便?
q里,我们看下jBPM-PVM概念和架?q也是jBPM整个目的核心所?
在代码中,我们惌得其他的lg?或者变量时,我们L使用Environment的API, 我们可以看下它的API的几个重要方?
Command
概念的引?主要是想Ҏ有的操作做一个封? 可以说上面每个Service的方法的实现都是通过实现一个Command来操?然后通过CommandService调用到后面具体的实现.
我们先来看下Command的接?
CommandService
q有一个特?是可以配置Interceptor,比如事务是在这一Service中来配置.看下CommandService的配|?
Wait State (也就是实现ExternalActivityBehaviour)的促?是通过Transition来完成的,来看下Transitionq个接口.
q里,我们利用PVM所提供的Model,来实C个基本的工作引?
正如我们之前所说的,ActivityBehaviour是整个流E的定义核心所?我们再看下它的API.
我们接下来实C个简单的WaitState,实现ExternalActivityBehaviour的接?
一L,我们来看一个简单的从a->b的流E?q次不同的是,a和b都是wait state.
在启动之?因ؓ执行到a的时?是一个wait state,所?当前的流E活动应该是指向a.
如果要到bq个activity,那么需要调?
接下?我们Z前面两种节点的实?来实C个稍微比较正式的程(loan process).
那么,我们p向了wiremoneyq个节点,因ؓwiremoney是个Display节点,所以它昄完后,默认的走向下一个节?archive.
具体的Event是由ObservableElement?span class="emphasis">EventName来构成的.
我们来实C个简单的EventListener, 叫PrintLn
<jbpm-configuration>
<import resource="jbpm.default.cfg.xml" />
<import resource="jbpm.tx.jta.cfg.xml" />
<import resource="jbpm.jpdl.cfg.xml" />
<import resource="jbpm.identity.cfg.xml" />
<import resource="jbpm.jobexecutor.cfg.xml" />
</jbpm-configuration>
<process-engine-context>
<repository-service />
<repository-cache />
<execution-service />
<history-service />
<management-service />
<identity-service />
<task-service />
<hibernate-configuration>
<cfg resource="jbpm.hibernate.cfg.xml" />
</hibernate-configuration>
...........
</process-engine-context>
<transaction-context>
<repository-session />
<db-session />
<message-session />
<timer-session />
<history-session />
<mail-session>
<mail-server>
<session-properties resource="jbpm.mail.properties" />
</mail-server>
</mail-session>
</transaction-context>
Object get(String key);
<T> T get(Class<T> type);
Object set(String key, Object value);
/**
* constructs the object.
* @param wireContext {@link WireContext} in which the object is created. This is also the {@link WireContext}
* where the object will search for other object that may be needed during the initialization phase.
* @return the constructed object.
*/
Object construct(WireContext wireContext);
/**
*called by the WireContext to initialize the specified object.
*/
void initialize(Object object, WireContext wireContext);
public interface Binding {
String getCategory();
/** does this binding apply to the given element? */
boolean matches(Element element);
/** translates the given element into a domain model java object.
* Use the parse to report problems.
*/
Object parse(Element element, Parse parse, Parser parser);
}
jbpm.cfg.xml ?gt; jBPMConfigurationParser -> Binding ?gt; Descriptor --> WireContext
public void testConfigurationServices() {
ProcessEngine processEngine = new Configuration()
.setXmlString(
"<jbpm-configuration>" +
" <process-engine-context>" +
" <repository-service />" +
" <execution-service />" +
" <management-service />" +
" </process-engine-context>" +
"</jbpm-configuration>"
)
.buildProcessEngine();
assertNotNull(processEngine);
assertNotNull(processEngine.getExecutionService());
assertNotNull(processEngine.getManagementService());
}
protected void parse(StreamInput streamSource) {
isConfigured = true;
JbpmConfigurationParser.getInstance()
.createParse()
.pushObject(this)
.setStreamSource(streamSource)
.execute()
.checkErrors("jbpm configuration " + streamSource);
}
<jbpm-configuration>
<process-engine-context>
<jboss-idm-identity-session-factory jndi="java:/IdentitySessionFactory" />
</process-engine-context>
<transaction-context>
<jboss-idm-identity-session realm="realm://JBossIdentity" />
</transaction-context>
</jbpm-configuration>
public abstract <T> T get(Class<T> type);
/** searches an object based on type. The search doesn take superclasses of the context elements
* into account.
* @return the first object of the given type or null in case no such element was found.
*/
public abstract <T> T get(Class<T> type, String[] searchOrder);
public interface RepositoryService {
NewDeployment createDeployment();
ProcessDefinitionQuery createProcessDefinitionQuery();
...
}
public interface ExecutionService {
ProcessInstance startProcessInstanceById(String processDefinitionId);
ProcessInstance signalExecutionById(String executionId);
...
}
public interface ManagementService {
void executeJob(long jobDbid);
JobQuery createJobQuery();
}
public interface Command<T> extends Serializable {
T execute(Environment environment) throws Exception;
}
public interface CommandService {
/**
* @throws JbpmException if command throws an exception.
*/
<T> T execute(Command<T> command);
}
<command-service>
<retry-interceptor />
<environment-interceptor />
<standard-transaction-interceptor />
</command-service>
public interface ActivityBehaviour extends Serializable {
void execute(ActivityExecution execution) throws Exception;
}
public interface ExternalActivityBehaviour extends ActivityBehaviour {
//handles an external trigger.
void signal(ActivityExecution execution, String signalName, Map<String, ?> parameters) throws Exception;
}
public interface Transition extends ObservableElement {
/** the activity from which this transition leaves. */
Activity getSource();
/** the activity in which this transition arrives. */
Activity getDestination();
}
public interface EventListener extends Serializable {
void notify(EventListenerExecution execution) throws Exception;
}
ClientProcessDefinition definition = ProcessDefinitionBuilder.startProcess("jeffProcess")
.startActivity("initial", new AutomaticActivity())
.initial()
.transition("first")
.endActivity()
.startActivity("first", new WaitStateActivity())
.transition("end", "endSignal")
.endActivity()
.startActivity("end", new AutomaticActivity())
.endActivity()
.endProcess();
public interface ActivityBehaviour extends Serializable {
void execute(ActivityExecution execution) throws Exception;
}
public class Display implements ActivityBehaviour {
String message;
public Display(String message) {
this.message = message;
}
public void execute(ActivityExecution execution) {
System.out.println(message);
}
}
隐式(implicit)的executeҎ执行序
如果我们在execute()Ҏ?没有昄的调用execution中的Ҏ,比如waitForSignal(),take(transition){方?那么默认的顺序是q样?
ClientProcessDefinition processDefinition = ProcessDefinitionBuilder.startProcess("helloworld")
.startActivity("a", new Display("Hello"))
.initial()
.transition("b")
.endActivity()
.startActivity("b", new Display("World"))
.endActivity()
.endProcess();
processDefinition.startProcessInstance();
Hello
World
public interface ExternalActivityBehaviour extends ActivityBehaviour {
//handles an external trigger.
void signal(ActivityExecution execution, String signalName, Map<String, ?> parameters) throws Exception;
}
public class WaitState implements ExternalActivityBehaviour {
public void execute(ActivityExecution execution) {
execution.waitForSignal();
}
public void signal(ActivityExecution execution,
String signalName,
Map<String, Object> parameters) {
execution.take(signalName);
}
}
ClientProcessDefinition pd = ProcessDefinitionBuilder.startProcess("helloworld")
.startActivity("a", new WaitState())
.initial()
.transition("b", "b-transition")
.endActivity()
.startActivity("b", new WaitState())
.endActivity()
.endProcess();
ClientProcessInstance instance = pd.startProcessInstance();
instance.isActive("a")
instance.signal("b-transition");
instance.isActive("b")
ClientProcessDefinition pd = ProcessDefinitionBuilder.startProcess("loanprocess")
.startActivity("submit loan request", new Display("submit a loan request"))
.initial()
.transition("evaluate", "evaluate-transition")
.endActivity()
.startActivity("evaluate", new WaitState())
.transition("wiremoney", "approve")
.transition("end", "reject")
.endActivity()
.startActivity("wiremoney", new Display("wire the money"))
.transition("archive")
.endActivity()
.startActivity("archive", new WaitState())
.transition("end", "done")
.endActivity()
.startActivity("end", new WaitState())
.endActivity()
.endProcess();
instance = pd.startProcessInstance();
现在? evaluateq个节点有两条支?一个是approve,指向wiremoney节点;另外一个是reject,直接走向end.
假设我们选择approveq条支\.
instance.signal("approve");
同样?因ؓarchive节点是个wait state,所以需要再一ơ的signal,才能走到endq个节点.
instance.signal("done");
public interface EventListener extends Serializable {
void notify(EventListenerExecution execution) throws Exception;
}
public interface EventListenerExecution extends OpenExecution {
void fire(String eventName, ObservableElement eventSource);
}
public class PrintLn implements EventListener {
String message;
public PrintLn(String message) {
this.message = message;
}
public void notify(EventListenerExecution execution) throws Exception {
System.out.println(message);
}
}
我们看下是怎么来定义一个具备有Events的ProcessDefinition:
ClientProcessDefinition pd = ProcessDefinitionBuilder.startProcess("ab")
.startActivity("a", new Display("Testing Event"))
.initial()
.transition("b")
.startEvent(Event.END)
.listener(new PrintLn("leaving a"))
.listener(new PrintLn("second message while leaving a"))
.endEvent()
.startEvent(Event.TAKE)
.listener(new PrintLn("taking transition"))
.endEvent()
.endActivity()
.startActivity("b", new WaitState())
.startEvent(Event.START)
.listener(new PrintLn("entering b"))
.endEvent()
.endActivity()
.endProcess();
]]>
一、 ?q行期环境就是一个餐?/strong>
1、 ?提供必要的服?br />
作ؓ一个餐馆,必须有厨师做饭我吃,必须有桌子和椅子。作行期环境同样如此Q我要发消息Q你得提供我发消息的ServiceQ我要获取节点Q务,你得扔给我TaskService?br />
2、 ?提供获取q些服务的统一方式
好吧Q我不会亲自到厨房告诉厨师我惛_什么(因ؓ我担心这样一来我会吃不下去)Q我也不会亲自到攉台给钱。这些服务有一个统一的获取方式:服务员。我?
吃什么和l̎Q告诉服务员卛_。关键是q一方式要统一Q要_单。Spring最懒,把服务给你全部注入了Q当然你也可以握住BeanFactory?
U纤l手Q一个一个的get?br />
3、 ?提供特定于我U程不安全的服务
我点了一盘鱼香肉丝,隔壁也点了一盘鱼香肉丝,l果服务员让我们吃同一盘鱼香肉丝。我立刻跌v来:靠,你们的服务不是线E安全的吗?QHibernate
的Session正是属于q么一U情况,需要环境进行隔,我的唯一职责是吃饭Q我的领域逻辑是如何优的q餐Qؓ此还要不断重构我吃饭的姿势哩?br />
好不Ҏ吃完饭,付完ƾ,正准备离场。服务员风度翩地走到我的n旁,我以有打折券供应Q结果是Q服务员姐d朱唇Q先生,ȝ您把吃剩的盘子清z完毕?br />
崩溃Q?br />
像数据库q接的打开Q关闭、事务的打开、提交等都属于运行期环境应该做的事情?br />
4、 ?其他的七七八?br />
杂事不少Q例如统一的事件机制、权限拦截等{?br />
二、 ?jBPM4的运行期环境
好吧Q先来看看如何徏立jBPM4的运行期环境Q?br />
Environment environment = environmentFactory.openEnvironment();
try {
everything available in this block
} finally {
environment.close();
}
两个关键的类QEnvironmentFactory和Environment?br />
EnvironmentFactory是全局的,在整个应用程序中保持一个实例即可?br />
Environment则是每次Ҏ调用则要new一个?br />
看看Environment的主要方法:
public abstract <T> T get(Class<T> type);
是的Qenvironment为我们的代码提供所需要的服务cd例?br />
那么Q如何获得environmentQ?br />
l箋看:
staticQ我喜欢也。方ѝ快P不管是在C、R上还是房上Q随处都可调用?br />
那么Qؓ什么Environment每次调用要new呢?
好吧Q当你需要获取数据库Session的时候,是不是每ơ都要new呢。Environment提供的服务里包括了非U程安全的数据库操作服务?br />
三、 ?jBPM4q行期环境的实现
1、JbpmConfiguration
JbpmConfiguration是jBPM4里最重要的类Q它是整个应用程序的入口。它实现了EnvironmentFactory接口?br />
JbpmConfiguration加蝲jBPMȝ配置文gQ还是大概扫一下这个配|文Ӟ
<process-engine-context>
<repository-service />
<repository-cache />
<execution-service />
<history-service />
<management-service />
<identity-service />
<task-service />
<hibernate-configuration>
<cfg resource="jbpm.hibernate.cfg.xml" />
</hibernate-configuration>
<hibernate-session-factory />
</process-engine-context>
<transaction-context>
<repository-session />
<pvm-db-session />
<job-db-session />
<task-db-session />
<message-session />
<timer-session />
<history-session />
</transaction-context>
</jbpm-configuration>
配置文g被分Z两部分,分别是:process-engine-context和transaction-context?br />
对应于两个IOC容器QWireContextQ的配置文g?br />
作ؓEnvironmentFactoryQJbpmConfiguration持有成品process-engine-context对应的IOC容器
Q全局的)实例Q持有半成品transaction-context的WireDefinition。当调用openEnvironmentҎ
ӞJbpmConfiguration会new EnvironmentQ然后将process-engine-context
IOC填充入environmentQ同时初始化transaction-context
IOCQƈ其也填充入environment。这样通过environment可以获得所有所需要的服务Q包括全局的和非线E安全的服务实例。也是
environment透过IOC容器提供了查扑U服务的能力?br />
2、与U程l定的environment
environment初始化之?避免参数传递得一塌糊涂的方式是environment与线E绑定。看Environment的代码:
static ThreadLocal<Stack<Environment>> currentEnvironmentStack = new ThreadLocal<Stack<Environment>>();
是的Q在openEnvironmentӞ有这么一行代码:
q样environment׃U程l定了,可以通过Environment.getCurrent()L调用了?br />
哪里有压q,哪里有放抗?br />
在environment.close()Ҏ里:
OKQ结束?br />
]]>
一、 ?Jbpm4 IOC容器介绍
IOC容器的目的是理lg和实现组件之间的解耦。和Spring里的BeanFactory对应QJbpm4里的接口是ContextQ具体实现则?
WireContext。Context实际在Jbpm4里有更多的含义,它与Environment一P共同构成了代码运行的q行期环境。在q个环境
里可以获取系l的lgQ更为重要的是提供了数据库连接(sessionQ和事务Q这个稍后会Ԍ?br />
先来看看Context接口的核心方法:
<T> T get(Class<T> type);
很明显,提供两种从容器里获取lg的方法,一U是通过nameQ一U是通过type?br />
对于IOC容器来说Q一般情况下都会提供一U加载的方式Q比如从xml文gq行加蝲、从资源文gq行加蝲。Jbpm4透过WireParser具备从xml加蝲的能力?br />
此外QWireContext通过一个Map~存初始化后的组件?br />
二、 ?Jbpm4 IOC容器实现
容器的实现有五个关键cd接口Q分别是QWireParser、Binding、Descriptor、WireDefinition和WireContext?br />
WireParserdxml文gQ同时WireParser会加载一pd的Binding(默认从jbpm.wire.bindins.xml文gd加蝲)?br />
Binding负责Ҏxml里元素的tagxml元素转换为对应的Descriptor?br />
Descriptor负责初始化对象。它们被d到WireDefinition?br />
WireDefinition被WireParserq回lWireContext。WireContext创徏对象时会讉KWireDefinition里的DescriptorQ同时将初始化对象的d委托lDescriptor自n?br />
需要注意的是:Jbpm4在初始化对象时有着四种{略Q分别是Qgq创建和初始化、gq创建和立刻初始化、立d建和延迟初始化、立d建和立刻初始化?br />
立刻创徏Q在WireContext创徏完毕后对象就已经创徏?br />
延迟创徏Q调用WireContext的getҎ获取该对象时才创对象?br />
初始化:一般完成对象属性的注入{操作?br />
三、 ?Jbpm4 IOC容器在Jbpm4里的应用
IOC容器在Jbpm4里最重要的作用就是加载Jbpm的ȝ配置文gQ默认是jbpm.cfg.xmlQ,q也是整个Jbpm应用的v炏V大概扫一下这个配|文Ӟ
<jbpm-configuration xmlns="http://jbpm.org/xsd/cfg">
<process-engine-context>
<repository-service />
<repository-cache />
<execution-service />
<history-service />
<management-service />
<identity-service />
<task-service />
<hibernate-configuration>
<cfg resource="jbpm.hibernate.cfg.xml" />
</hibernate-configuration>
<hibernate-session-factory />
</process-engine-context>
<transaction-context>
<repository-session />
<pvm-db-session />
<job-db-session />
<task-db-session />
<message-session />
<timer-session />
<history-session />
</transaction-context>
</jbpm-configuration>
可以看到配置文g被分Z两部分,分别是:process-engine-context和transaction-context。在实际应用中,它们
分别对应着两个不同的WireContext:ProcessEngineContext和TransactionConext?
ProcessEngineContext覆盖了jbpm4里最重要的服务类Q这些类是全局唯一的,当然QProcessEngineContext也是
独此一份。本是同根生Q命q各不同。TransactionConext则是在每ơopenEnvironment旉新创建,因ؓ其包含了数据库连接和
事务?br />
贯穿于整个Jbpm4中,q两个Context被压到Environment里(Environment和线E绑定)Q在M需要的地方都能提供一条龙的服务。于是,在很多领域类里,利用q些服务实现充血模型是很顺理成章的一件事了?br />
ȝ: ProcessEngineContextl引擎领域模型提供全局的组件查找;TransactionConext提供数据库相x务?br />
]]>
jBPM d理Q参与模式一?br />
Posted on 2009-03-30 09:41:00.0 by Heiko Braun
Since 4.0.0 Beta1
重新回顾jBPM中的d理模型Q引q了一个新的概念:d参与。参与模型描qCidentiesQ用hl)和Q务在实际完成中的参与cdQ?br />
CZ1Q用户和业务理员参?br />
在此与这个模型非帔R合的一个通常的案例是在一个实际执行Q务的用户和一个业务管理员监控q展情况之间的区别。依赖于参与cdQ某些规则将实际起作用,而其它的确保Q务在l定的约束内被执行(例如Q优先Q预定日期等Q?/p>
CZ2Q具有不同的参与cd的Q务利益相兌?br /> 另一个例子可能是利益相关者监控Q务的实际输出Q或者是在一个Q务上互相协作的不同参与者之间的委托。在q个案例中,一个Q务的发v人,一个候选h执行q个工作q且最l的与受者可能是不同的参与类型?/p>
The TaskService API已经反映了那些变化:
org.jbpm.TaskService{ [...] /** * retrieves a list of tasks for a user * and a particular {@link org.jbpm.task.Participation} type * * @see org.jbpm.TaskQuery */ /** * retrieves a list of tasks for a group * and a particular {@link org.jbpm.task.Participation} type * * @see org.jbpm.TaskQuery */ }
目前Q我们封装了一些默认的参与cdQ其中仅仅支持“候选h”模型,但是你可以期待这些将在不久的来被扩展实现?/p>
org.jbpm.task.Participation{ [...] String CANDIDATE = "candidate"; IdentityRef getIdentityRef(); /** see constants for default participations */ }
敬请期待?/p>
新的
BPM
控制台已l发布了W一个里E碑Q我很高兴利用这个机会介l它最重要的改变及新特性?/span>
程定义理
q移?/span> GWT
来多?/span> JBoss 目正在q移?/span> GWT 。他们这么做的原因是Q?/span>
Ø 如果你熟?/span> Java 的开发ƈ且不xZ一?/span> web 开发的专家Q那?/span> GWT 是一个比较好的选择。?/span> GWT 你可以粘?/span> eclipse Q发动一个调试ƈ且编写单元测试;
Ø 它有一套丰富的 UI H口部gQ你可以立马使用。这U窗口部仉过自己的实C证了一个统一的外观和感觉Q?/span>
其它的比较好的成功的 GWT 实施的例子就?/span> Drools 控制C。( JBoss 开源的规则引擎Q?/span>
程实例理
GWT 已经非常的流行了Q它允许通过目来集成控制台。另一斚w的媄响就是,你可以轻村֜开发一个完整的 GWT 应用Q或者是一部分Qƈ且同已经存在?/span> web 应用整合在一赗D例来_q样可以允许用户?/span> BPM 控制台的d理功能嵌入C们自q内网应用中?/span>
程实例l节
BAM Q业务活动监控)?/span> BI Q商业智能)功能的改q?/span>
或许当前控制台的最大缺点就是缺?/span> BAM ?/span> BI Ҏ?/span>
工作负蝲概览
改进 BAM ?/span> BI 不可能在一天内马上实现Q但是你可以期待在早期的发布版本中看到第一个度量及状态监控,在我们实C个功能齐全的 BAM 控制台的q程中,我们正尽力增加更多的零星的功能。因与服务zd监控目中的功能和技术发生重叠,感兴的读者可以关注一?/span> SAM ?/span>
性能度量
如何q移
首先Q我们将Z GWT 提供一个现?/span> jBPM 控制台的替代。它仍然保持当前的功能Ҏ,但是会增?/span> BI 功能。最初我们会q jBPM3 的后端功能,然后逐渐地利?/span> SAM 的空间来丰富它,甚至最l完全代替它?/span>
程囑Ş视图
l箋x。下ơ我们将要详l说明实现细节,包括Q?/span> gchart Q?/span> gwt ?/span> gwt-ext ?/span>
说明Q此文英文链接:http://jboss-overlord.blogspot.com/2008/08/first-glimpse-at-new-bpm-console.html
jBPM3 vs jBPM4
JBoss Goup 目前已经发布?/span> jBPM4 Alpha1 版本Q在版本 4 中最大的变化是引入 PVM Q流E虚拟机Q的概念Q而引擎内部的调度法中重要的 Token 机制Q在新版中也L了,U观整个代码Q变化可以说非常的大Q笔者接下来p着来比较一下这U变化,让大家能有个直观的认识。当?/span> Jbpm4 ?/span> JBoss 的官方网站上?/span> Road map 中,在今q的 7 ?/span> 1 h会发布第一个正式版本,因此后箋可能q会有变化?/span>
1?/span> 程定义对象的变化:
Jbpm3 程定义对象关系图:
图一 jbpm3程定义对象关系?/span>
从上图我们可以看 jbpm3 中, GraphElement 是流E图中所有流E元素的父对象,而整个流E是?/span> ProcessDefinition ?/span> Node ?/span> Transition 三个主要对象构成Q?/span>
图二 PVM实体对象关系?/span>
从上囑֏以看出,׃ PVM 概念的引入,所以在 jbpm3 中的 Graph 包在 jbpm4 中被U除了。在 pvm 中,在设计期Q所有节点元素的父类?/span> ProcessElementImpl Q流E的主要l成元素 Nodelmpl ?/span> TransitionImpl ?/span> ProcessDefinitionImpl ?/span> EventImpl 则都直接或间接承自 ProcessElementImpl 。在q行期: jbpm4 把流E的q行期行为定义ؓ执行行ؓQ?/span> ExecutionImpl Q及原子操作行ؓQ?/span> AtomicOperation Q其具体实现?/span> ExecuteNode ?/span> ProceedToDestination ?/span> TakeTranstion ?/span> MoveToParentNode ?/span> MoveToChildNode ?/span> signal Q,其中 ExecutionImpl 是流E实例、活动实例、事件监听器的所有执行期行ؓ的实现类?/span>
图三 jpdl q行期活动实体对象关pd
上图?/span> jbpm4 在运行期的活动实例对象关pdQ从图中我们可以看出Q在q行期, jbpm4 中定义了两个zd接口 Activity ?/span> ExternalActivity Q其?/span> ExternalActivity l承?/span> Activity ?/span> Activity 是所有自动活动节点的父接口,其实现类?/span> JpdlActivity Q?/span> JpdlActivity 又衍生出了?/span> StartActivity ?/span> JoinActivity ?/span> ForkActivity ?/span> EndActivity ?/span> CreateTimerActivity ?/span> JavaActivity ?/span> EsbActivity {实例活动对象。?/span> ExternalActivity 是具有等待状态的zdQ?/span> StateActivity Q父接口Q像人工zd TaskActivity 是实现了此接口?/span>
2?/span> 核心引擎的调度算?/span>
Jbpm3 的核心调度算法是Z Token 机制的,在运行期q个 Token ?/span> Node Instance 之间{Q依?/span> Token 的触发来推进程。具体的调度机制Q可参加胡长城的文章Q?/span> http://blog.csdn.net/james999/archive/2007/09/02/1769592.aspx Q;其实q个 Token 来自?/span> Pertri-net Q感兴趣的读者可以去?/span> Pertri-net 中的 Token ?/span> Place ?/span>
囑֛ jbpm3引擎调度?/span>
Jbpm4 则去掉了 Token Q那么它的核心调度机制是怎样实现的呢Q?/span>
图五 jbpm4程启动序列?/span>
囑օ jbpm4 程推进序列?/span>
图五是在 jbpm4 中启动一个流E实例的执行序列图,囑օ是节Ҏq的执行序列图,从上面两个图中我们可以看到核心的调度是依?/span> Execution 的{UL实现的( ExecutionImpl 可以?/span> ActivityExecution ?/span> ClientProcessInstance ?/span> EventListenerExecution 的实例)Q?/span> Execution 实际上就是取代了 Jbpm3 中的 Token Q?/span> Execution 的{Ud际上是Ҏ状态机的变q( ActivityExecution ?/span> ClientProcessInstance ?/span> EventListenerExecution 实例之间的切换)加上调用相应的原子操作: ExecuteNode ?/span> MoveToChildNode ?/span> MoveToParentNode ?/span> ProceedToDesitination ?/span> Signal ?/span> TakeTransition Q详?/span> pvm/internal/model/op 包下的相关类Q来实现的。所?/span> Execution 实例的集合及有向囑֮际上是q行期的路径?/span>
3?/span> Event-Action 机制的变?/span>
?/span> jbpm3 中是Z Event-Action 机制来实C件与动作的触发的Q但是在 jbpm4 中则采用观察者模式来触发事g的。所有用戯己定义的动作Q全部要实现 EventListener 接口Q这些动作作为监听者(是事g Event 的观察?/span> Observer Q注册到相应的流E定义对象上Q?/span> ProcessElement 或?/span> Node Q,而事?/span> Event 则作观察的对象(实际上就?/span> Observerable Q,实际上在 jbpm4 中专门定义出了一个对?/span> ObservableElementImpl Q流E定义中?/span> NodeImpl ?/span> TransitionImpl ?/span> ProcessDefinitionImpl 均承自此对象,因此q些元素本n可以作?/span> Observerable 而被观察者来监控?/span>
4?/span> 客户端接口的变化
?/span> jbpm4 中对客户端的接口l一?/span> 7 个服务接口: ProcessService ?/span> ExecutionService ?/span> CommandService ?/span> TaskService ?/span> ManagementService ?/span> HistoryService ?/span> IdentityService Q这 7 个接口可以从 ProcessEngine 接口中获得, jbpm4 在启动的q程中由 JbpmConfiguration 负责构徏引擎?/span>
Ø ProcessService- 程定义的服务接口,包括ҎE定义的部v、查询、删除操作;
Ø ExecutionService- 执行服务接口Q包括启动流E、实例推q、设|变量等操作Q?/span>
Ø CommandService-Command 模式的服务接口,实际上就是将客户端的h全部装在一个调用接口中Q然后由q个接口去调?/span> Command 接口的众多实玎ͼ StartExecutionCmd ?/span> SignalCmd ?/span> SetVariablesCmd ?/span> GetTimersCmd ?/span> DeployCmd ?/span> NewTaskCmd ?/span> SubmitTask ?/span> ExecuteJobCmd {等Q具体可参加 pvm/internal/cmd Q?/span> task/internal/cmd 包及其它包下实现 Command 接口的类Q,q是典型?/span> Command 模式的应用,感兴的读者可以去了解设计模式中的 Command 模式Q?/span>
Ø TaskService- 人工zd的服务接口,包括对Q务的创徏、提交、查询、保存、删除等操作Q?/span>
Ø ManagementService-web 理控制台的服务接口Q目前只有获得消息及计时器的接口实现Q?/span>
Ø HistoryService- 目前有对历史库中的流E实例、活动实例进行查询、某个流E定义中的所有活动的q_持箋旉、某个流E定义中的某个活动实例的转移的执行次?/span>
Ø IdentityService- 用户、组、成员关pȝ相关操作Ҏ
5?span style="font-family: "Times New Roman"; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;"> 历史库的加入
jBPM3 中数据库设计一直是我比较诟病的地方Q尤其是其实例数据库没有设计历史库的概念q按照办l状态将q行l束的实例数据归入历史库Q在q种情况下它的实例数据库׃随着旉而无限膨胀Q这阻了它的真实应用Q而在 jBPM4 的最C码中Q注?/span> Alpha1 q没有出玎ͼQ历史库的相兛_能代码竟然出CQ详?/span> ExecutionImpl 最C码中?/span> fireHistoryEvent Ҏ及一pd?/span> historyXXX Ҏ。在 ActivityBehaviour ?/span> execute Ҏ中加入了 historyTaskStart Ҏ的调用?/span> signal Ҏ中加入了 historyTaskEnd Ҏ的调用,而以?/span> 2 个方法在 ExecutionImpl 中都是以历史事gQ?/span> HistoryEvent ?/span> 4 个实现子c?/span> ProcessInstanceStart ?/span> ProcessInstanceEnd ?/span> ActivityStart ?/span> ActivityEnd 分别用作程实例的创建结束期、活动实例的创徏l束期的历史数据处理Q的触发机制来实现的Q也是在整个流E实例执行的q程中,都加入了对将q行数据存入历史库的历史事gQ?/span> HistoryEvent Q的触发。这样实例列表的查询可以只查询历史库。不q这里很遗憾的是Q这个事件没有同时清除运行库的数据,q样q是会造成q行库的无限膨胀问题?/span>