jBPM5.4官方文档翻译—第五章核心引擎;API

felix_alone2012 2013-09-14
5.1 jBPM API
   5.1.1  知识库(Knowledge Base)
  5.1.2 Session对象
  5.1.3 事件
5.2  基于知识的API

本章引入的API,你可以用它来加载流程并执行它们。关于如何定义流程的更多信息,可以参考BPMN2.0

要和流程引擎交互的话(例如,启动一个流程),你需要建立一个session对象。这个session对象可以和流程引擎进行通信。一个session对象需要保持对一个知识库knowledge base的引用,而在知识库对象中包含了对所有的相关的流程定义的引用。无论何时,都可以使用知识库对象来查找流程定义中的一些信息。要创建一个session对象,你首先需要创建一个知识库对象,加载所有必要的流程信息(流程定义文件的来源很多,可以是类路径、文件系统或是流程仓库),然后来实例化一个session对象。

一旦你建立了一个session对象后,你可以使用它来开始流程的执行。无论何时开始了一个流程后,就会创建一个新的流程实例(与那个流程定义相关的),这个流程实例对象会维护流程的特定实例的状态。

例如,假设你想要编写一个应用来处理销售订单。你可以定义一个或多个流程定义,这些流程定义可以决定订单可以怎么被处理。当启动了你的应用时,你会首先需要创建一个知识库对象来包含这些流程定义。然后,你就可以创建一个基于这个知识库对象的session对象,这样的话,每一个销售订单的请求来了的话,就会启动一个新的流程实例。这个流程实例包含了特定的一个销售请求的流程的状态信息。

一个知识库对象可以被多个session对象来共享,通常在应用启动的一开始只会创建一次,因为创建一个知识库对象是相当重量级的,因为这会涉及到解析并编译流程定义。而知识库对象可以被动态的改变(这样你就可以在运行时动态的添加或移除流程了)。

Session对象可以基于一个知识库对象来创建,并用于执行流程以及和引擎进行交互。你可以创建很多的独立的session对象,而创建一个session对象是相当轻量级的。要创建多少个session对象完全取决于你。通常来说,只要创建一个session对象就够了,这样你可以在应用的多个地方调用它。如果你想要有多个独立的流程单元,你就可以创建多个session了,例如每一个会员的所有流程都要和其他的会员的流程独立开来的话,你就可以为每个会员创建一个独立的session对象了。或者你想要使用多个session对象来实现拓展的需要。如果你真不知道要怎么做的话,你可以简简单单的创建一个知识库对象来包含你所有流程定义,并创建一个session对象来执行你的所有的流程。

5.1 jBPM的API
jBPM提供的用户可以交互的API和实际实现类是分开来的。公共的API暴漏了大部分的特性,一般的用户可以放心使用,这些特性在后续的jBPM版本中会进一步加强。专业用户还可以访问内部的实现类,但是他们要知道他们要用这些类来干什么,这些内部的API在未来还会被调整。

如上所述,jBPM可以用来:1)创建一个知识库对象来包含你的流程定义,2)创建一个session对象来启动一个新的流程实例、在存在的流程实例上进行流转和发信号、注册监听器等等。

5.1.1 知识库对象
jBPM的API首先允许你创建一个知识库对象。这个知识库对象应该包含所有的流程定义以便后续被session执行到。要创建知识库对象,使用一个知识构建器对象来从各种资源中加载流程(例如,从类路径或文件系统),然后从构建器中创建一个信息的知识库对象。下面的代码段告诉你如何创建一个知识库对象,仅仅包含了一个流程的定义(是从类路径中加载一个资源的)。
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource("MyProcess.bpmn"), ResourceType.BPMN2);
KnowledgeBase kbase = kbuilder.newKnowledgeBase();
ResourceFactory类有很多类似的方法可以用来从文件系统、URL、输入流或Reader等等中加载流程文件。

5.1.2 session对象
一旦加载了知识库对象,你就要创建一个与引擎交互的session对象。这个session对象然后可以用来启动一个新流程、触发事件等等。下面的代码段告诉你如何基于前面创建好的知识库对象来简单创建一个session,并根据Id来发起一个流程。
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
ProcessInstance processInstance = ksession.startProcess("com.sample.MyProcess");
ProcessRuntime接口定义了session中用来和流程进行交互的所有的方法,如下所示:
/**
     * Start a new process instance.  The process (definition) that should
     * be used is referenced by the given process id.
     *
     * @param processId  The id of the process that should be started
     * @return the ProcessInstance that represents the instance of the process that was started
     */
    ProcessInstance startProcess(String processId);

    /**
     * Start a new process instance.  The process (definition) that should
     * be used is referenced by the given process id.  Parameters can be passed
     * to the process instance (as name-value pairs), and these will be set
     * as variables of the process instance.
     *
     * @param processId  the id of the process that should be started
     * @param parameters  the process variables that should be set when starting the process instance
     * @return the ProcessInstance that represents the instance of the process that was started
     */
    ProcessInstance startProcess(String processId,
                                 Map<String, Object> parameters);

    /**
     * Signals the engine that an event has occurred. The type parameter defines
     * which type of event and the event parameter can contain additional information
     * related to the event.  All process instances that are listening to this type
     * of (external) event will be notified.  For performance reasons, this type of event
     * signaling should only be used if one process instance should be able to notify
     * other process instances. For internal event within one process instance, use the
     * signalEvent method that also include the processInstanceId of the process instance
     * in question.
     *
     * @param type the type of event
     * @param event the data associated with this event
     */
    void signalEvent(String type,
                     Object event);

    /**
     * Signals the process instance that an event has occurred. The type parameter defines
     * which type of event and the event parameter can contain additional information
     * related to the event.  All node instances inside the given process instance that
     * are listening to this type of (internal) event will be notified.  Note that the event
     * will only be processed inside the given process instance.  All other process instances
     * waiting for this type of event will not be notified.
     *
     * @param type the type of event
     * @param event the data associated with this event
     * @param processInstanceId the id of the process instance that should be signaled
     */
    void signalEvent(String type,
                     Object event,
                     long processInstanceId);

    /**
     * Returns a collection of currently active process instances.  Note that only process
     * instances that are currently loaded and active inside the engine will be returned.
     * When using persistence, it is likely not all running process instances will be loaded
     * as their state will be stored persistently.  It is recommended not to use this
     * method to collect information about the state of your process instances but to use
     * a history log for that purpose.
     *
     * @return a collection of process instances currently active in the session
     */
    Collection<ProcessInstance> getProcessInstances();

    /**
     * Returns the process instance with the given id.  Note that only active process instances
     * will be returned.  If a process instance has been completed already, this method will return
     * null.
     *
     * @param id the id of the process instance
     * @return the process instance with the given id or null if it cannot be found
     */
    ProcessInstance getProcessInstance(long processInstanceId);

    /**
     * Aborts the process instance with the given id.  If the process instance has been completed
     * (or aborted), or the process instance cannot be found, this method will throw an
     * IllegalArgumentException.
     *
     * @param id the id of the process instance
     */
    void abortProcessInstance(long processInstanceId);

    /**
     * Returns the WorkItemManager related to this session.  This can be used to
     * register new WorkItemHandlers or to complete (or abort) WorkItems.
     *
     * @return the WorkItemManager related to this session
     */
    WorkItemManager getWorkItemManager();

5.1.3 事件
Session中提供了用于注册和移除监听器的方法。一个ProcessEventListener可以被用来监听流程相关的事件、如启动流程、结束流程、进入一个节点、离开一个节点等等。下面列出了ProcessEventListener类中不同的监听方法。方法中的event对象提供了能被访问到的相关信息,如,与这个事件有联系的流程实例和节点实例。你可以使用这个API类注册你自己的事件监听器。
public interface ProcessEventListener {

  void beforeProcessStarted( ProcessStartedEvent event );
  void afterProcessStarted( ProcessStartedEvent event );
  void beforeProcessCompleted( ProcessCompletedEvent event );
  void afterProcessCompleted( ProcessCompletedEvent event );
  void beforeNodeTriggered( ProcessNodeTriggeredEvent event );
  void afterNodeTriggered( ProcessNodeTriggeredEvent event );
  void beforeNodeLeft( ProcessNodeLeftEvent event );
  void afterNodeLeft( ProcessNodeLeftEvent event );
  void beforeVariableChanged(ProcessVariableChangedEvent event);
  void afterVariableChanged(ProcessVariableChangedEvent event);

}
注意下之前和之后事件:这些事件通常充当一个栈,也就是说,任何事件发生是因为前一个事件直接引起的,这些事件一般在那个事件的之前或之后发生的。例如,如果离开一个节点后,下一个节点被触发了,这个事件的触发时机会在被离开的那个节点的beforeNodeLeftEvent  和 afterNodeLeftEvent  事件之间(因为只要离开了第一个节点,下一个节点的事件就会被触发)。根据这个机制,我们就可以更容易的理解事件的发生关系了。类似的,所有节点事件的触发和节点的离开事件都会在流程的beforeProcessStarted  和afterProcessStarted  事件之间。通常,如果你只想要在一个特定的事件发生时被通知到,你就应该只关注before事件(因为在事件实际发生前这个事件会快速发生)。如果你只看after事件,你就有可能看到事件的触发貌似是一种错误的顺序,因为after事件的触发类似于栈(这个after事件只会在该发生的相关事件都发生了之后才会发生)。什么时候需要用到after事件呢?仅当你想要确保所有与这个事件相关的处理都结束了(例如,当你想要在发起一个特定的流程实例完成之后得到一个通知)。

同时也要注意了,不是所有的节点都会生成节点触发事件和(或者)节点离开事件。依据节点的类型,一些节点只会生成节点离开事件,其他一些只会生成节点触发事件。捕获到中间事件这样一个事件并不能算是触发事件(它们只能算是离开事件,因为它们并不是被其他的节点触发的,更不用说是被外部激活的)。同样的,抛出中间事件的这样一个事件不是离开事件(它们只是触发事件,因为它们并不是真正的离开,因为它们没有离开的连接)。

jBPM创造性的提供了一个监听器,它可以用来创建一个审计日志(从控制台输出,或者输出到文件系统中的一个文件中)。这个审计日志包含了所有的在运行时发生的不同事件,这样就以便侦听流程执行过程中到底发生了些什么事。注意下,这些日志记录仅仅应该用来调试用。下面是默认支持的日志实现:
控制台日志:这个日志会把所有的事件输出到控制台。
文件日志:这个日志会把所有的事件用XML格式输出到一个文件中。这个日志可以在IDE工具中使用,用它生成一个树状的事件视图来反映流程执行过程中发生的事件。
单线程文件日志:因为一个文件日志在将事件输出到磁盘上时,只是在关闭日志或日志对象已经预先知道会发生哪些事件的时候。它并不能在运行时环境中用来调试流程。而单线程文件日志会在流程运行时每隔一个时间间隔将事件写到文件中,这样就有可能在调试流程时,使用日志实时的反映流程的执行现状。

KnowledgeRuntimeLoggerFactory  类可以添加一个日志对象到session中,如下所示。当创建一个控制台日志对象时,与这个日志对象相关的knowledge session对象必须要作为一个参数传进去。文件日志对象创建时要传入需要的日志文件名称,而单线程文件日志需要传入一个间隔时间(毫秒数)以便隔一段时间保存一下事件。在应用结束的时候需要关闭日志对象。
KnowledgeRuntimeLogger logger =
    KnowledgeRuntimeLoggerFactory.newFileLogger( ksession, "test" );
// add invocations to the process engine here,
// e.g. ksession.startProcess(processId);
...
logger.close();
基于文件的日志对象创建的日志文件包含了一个XML格式的概述,来描述运行时发生的所有事件。它可以在Eclipse中打开,只需要使用Drools Eclipse插件的审计视图打开,就会看到事件以树状结构输出。事件相关的before和after事件被显示为改事件的子节点。下面的截屏就是一个简单的例子,流程发起后,开始节点被触发、接下来是Action节点、End节点,最后是流程结束。

5.2 基于知识的API
你也许注意到了,jBPM暴漏出来的API是一个基于知识的API。也就是说,它不仅仅关注于流程,还会深入反映出其他一些知识类型被加载了。仅仅关注于流程的用户不多,也就是说,取代类似于ProcessBase和ProcessSession的类,你现在使用的是KnowledgeBase和KnowledgeSession。

然而,如果你想要在你的应用中使用业务规则或复杂的事件处理,基于知识的API允许用户添加不同类型的资源,像流程定义、规则定义,规则库对象都会以同等的方式来加载这些资源。这样的话,用户可以使用Drools Expert(用于业务规则)或Drools Fusion(用于事件处理)或是整合这些不同类型的知识对象来协同jBPM进行流程设计、开发、部署和执行,因为jBPM5的API和工作使用了一种统一的方式来管理这些不同类型的知识对象。
lijiejava 2013-09-17
Global site tag (gtag.js) - Google Analytics