Chapter 5. API
Table of Contents
- Engine API(引擎API )
- Exception strategy(异常处理策略)
- Unit testing(单元测试)
- The process engine in a webapplication(在Web应用里的流程引擎)
- Process Virtual Machine API(流程虚拟机API)
- Expressions(表达式)
Engine API(引擎API )
The engine API is the most common way of interacting with Activiti. The central starting point is the ProcessEngine
, which can be created in several ways as described in theconfiguration section. From the ProcessEngine, you can obtain the various services that contain the workflow/BPM methods. ProcessEngine and the services objects are thread safe. So you can keep a reference to 1 of those for a whole server.
引擎API是与Activiti交互的最公共的方式。 ProcessEngine
是中心启动点。如在 configuration section章节里描述的那样,它可以用几种方法建立。从 ProcessEngine
里能够获得包括工作流/BPM方法的各种各样的服务。 因为ProcessEngine
和服务对象是线程安全的,所以能够为整个服务器范围保留这些服务的引用为1。
ProcessEngine processEngine = new ProcessEngineBuilder()
.configureFromPropertiesResource(configurationResource)
.buildProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
ManagementService managementService = processEngine.getManagementService();
IdentityService identityService = processEngine.getIdentityService();
HistoryService historyService = processEngine.getHistoryService();
FormService formService = processEngine.getFormService();
The names of the service are quite self-explanatory. For detailed information on the services and the engine API, see the javadocs.
服务名是自解释的,相当明了。要了解服务和引擎API的详情,参见 the javadocs.
Exception strategy(异常处理策略)
The base exception in Activiti is the org.activiti.engine.ActivitiException
, an unchecked exception. This exception can be thrown at all times by the API, but 'expected' exceptions that happen in specific methods are documented in the the javadocs. For example, an extract from TaskService
:
Activiti的基本异常是org.activiti.engine.ActivitiException, 一个unchecked异常。尽管在所有时间API都可抛出这个异常,但是 某些方法产生'expected'异常。在 the javadocs里面对这种异常进行了文档化。例如,下面是一个从 TaskService
抽取一段代码:
/**
* Called when the task is successfully executed.
* @param taskId the id of the task to complete, cannot be null.
* @throws ActivitiException when no task exists with the given id.
*/
void complete(String taskId);
In the example above, when an id is passed for which no task exists, an exception will be thrown. Also, since the javadoc explicitly states that taskId cannot be null, anActivitiException
will be thrown when null
is passed.
在上例,当传递一个不存在的任务的id,将会抛出一个异常。当然,javadoc 明明白白说明taskId不能为null,当传递null时,将抛出 ActivitiException
。
Even though we want to avoid a big exception hierarchy, the following subclasses were added which are thrown in specific cases:
尽管我们竭力避免一个庞大的异常层次体系,但还是在特殊情况下增加了如下的子类。
-
ActivitiWrongDbException:
Thrown when the Activiti engine discovers a mismatch between the database schema version and the engine version.
ActivitiWrongDbException:
当Activiti引擎发现数据库结构版本和引擎版本之间不匹配时,将抛出这个异常。
-
ActivitiOptimisticLockingException:
Thrown when an optimistic locking occurs in the datastore caused by concurrent access of the same data entry.
ActivitiOptimisticLockingException:
当数据库里由对相同的数据项并发访问导致乐观锁定时发生。
Unit testing(单元测试)
Business processes are an integral part of software projects and they should be tested in the same way normal application logic is tested: with unit tests. Since Activiti is an embeddable Java engine, writing unit test for business processes is as simple as writing regular unit tests.
业务流程是软件项目的一个集成部分,那么应当采用正常应用逻辑测试 :采用单元测试。因为Activiti是一个嵌入式Java引擎,所以编写业务流程的单元测试像编写正常的单元测试一样简单。
Activiti supports both Junit versions 3 and 4 style of unit testing. In the Junit 3 style, the org.activiti.engine.test.ActivitiTestCase must be extended. This will make the processEngine and the services available through protected member fields. In the setup() of the test, the processEngine will be initialized by default with the activiti.cfg.xmlresource on the classpath. To specify a different configuration file, override the getConfigurationResource() method. Process engines are be cached statically over multiple unit tests when the configuration resource is the same.
Activiti支持单元测试的Junit版本3和4风格。在Junit 3风格里,必须扩展org.activiti.engine.test.ActivitiTestCase。这将使得通过保护成员字段获得流程引擎和服务。在测试的setup()里,缺省地流程引擎将通过classpath上的 activiti.cfg.xml 资源初始化流程引擎。为了指定不同的 配置文件,覆盖 getConfigurationResource() 方法。当配置资源是相同时,流程引擎静态地缓存多个单元测试。
By extending ActivitiTestCase, you can annotate test methods with org.activiti.engine.test.Deployment. Before the test is run, a resource file of the formtestClassName.testMethod.bpmn20.xml in the same package as the test class, will be deployed. At the end of the test, the deployment will be deleted, including all related process instances, tasks, etc. The Deployment annotation also supports setting the resource location explicitly. See the Javadocs for more details.
通过扩展 ActivitiTestCase ,你能用org.activiti.engine.test.Deployment标注测试方法。在运行测试以前,将同一包里部署的 testClassName.testMethod.bpmn20.xml作为测试类。在测试结束,将删除包括所有相关的流程实例,任务等等部署。Deployment 标注也支持显式设置资源的位置。详情参见 the Javadocs。
Taking all that in account, a Junit 3 style test looks as follows.
将上面所有的情况考虑进去,Junit 3风格的测试方式如下所示。
public class MyBusinessProcessTest extends ActivitiTestCase {
@Deployment
public void testSimpleProcess() {
runtimeService.startProcessInstanceByKey("simpleProcess");
Task task = taskService.createTaskQuery().singleResult();
assertEquals("My Task", task.getName());
taskService.complete(task.getId());
assertEquals(0, runtimeService.createProcessInstanceQuery().count());
}
}
To get the same functionality when using the Junit 4 style of writing unit tests, the org.activiti.engine.test.ActivitiRule Rule must be used. Through this rule, the process engine and services are available through getters. As with the ActivitiTestCase (see above), including this Rule will enable the use of the org.activiti.engine.test.Deploymentannotation (see above for an explanation of its use and configuration) and it will look for the default configuration file on the classpath. Process engines are statically cached over multiple unit tests when using the same configuration resource.
当使用Junit 4风格编写单元测试时,为了获得相同的功能,必须使用 org.activiti.engine.test.ActivitiRule 规则。采用这个规则,通过getter获得流程引擎和服务。采用 ActivitiTestCase(如上),包括使用 org.activiti.engine.test.Deployment (它的使用和配置如上)标注的这个规则,它将在classpath上寻找缺省的配置。当使用相同配置资源时,流程引擎静态地缓存多个单元测试。
Following code snippet shows an example of using the Junit 4 style of testing and the usage of the ActivitiRule.
下面代码片段展示了采用的Junit 4风格的测试和 ActivitiRule用法的示例。
public class MyBusinessProcessTest {
@Rule
public ActivitiRule activitiRule = new ActivitiRule();
@Test
@Deployment
public void ruleUsageExample() {
RuntimeService runtimeService = activitiRule.getRuntimeService();
runtimeService.startProcessInstanceByKey("ruleUsage");
TaskService taskService = activitiRule.getTaskService();
Task task = taskService.createTaskQuery().singleResult();
assertEquals("My Task", task.getName());
taskService.complete(task.getId());
assertEquals(0, runtimeService.createProcessInstanceQuery().count());
}
}
The process engine in a webapplication(在Web应用里的流程引擎)
The ProcessEngine
is a thread-safe class and can easily be shared among multiple threads. In a webapplication, this means it is possible to create the process engine when the container boots and shut down the engine when the container goes down.
ProcessEngine
是一个线程安全的类并能在多个线程里面轻易共享。在一个Web应用里面,这意味着当容器引导和关闭引擎时,可能建立这个流程引擎。
The following code snippet how this is done in a regular Servlet environment, using a ServletContextListener:
下列代码通过使用 ServletContextListener,展示了在一个普通的 Servlet 环境下如何完成这个工作的。
public class ProcessEnginesServletContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
ProcessEngines.init();
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
ProcessEngines.destroy();
}
}
The contextInitialized will delegate to ProcessEngines.init()
. That will look for activiti.cfg.xml
resource files on the classpath, and create a ProcessEngines
for the given configurations (eg. multiple jars with a configuration file). If you have multiple such resource files on the classpath, make sure they all have different names. When the process engine is needed, it can be fetched using
将contextInitialized委托给 ProcessEngines.init()
。它将在classpath上寻找 activiti.cfg.xml
资源文件,并且为给定的配置建立 ProcessEngines
。如果在classpath上具有多个如此的资源文件,确保它们都具有不同的名字。当需要流程引擎时,它将采用下面的方法来取得
ProcessEngines.getDefaultProcessEngine()
or
或
ProcessEngines.getProcessEngine("myName");
Of course, it is also possible to use any of the variants of creating a process engine, as described in the configuration section.
当然,采用建立流程引擎的变体也是可能的。如 configuration section所示。
The contextDestroyed of the context-listener delegates to ProcessEngines.destroy()
. That will properly close all initialized process engines.
context-listener 的contextDestroyed 委托给ProcessEngines.destroy()
。那将正常关闭所有以初始化的流程引擎。
Process Virtual Machine API(流程虚拟机API)
The API exposes the POJO core of the Process Virtual Machine. Reading and playing with it is interesting for education purposes to understand the internal workings of Activiti. And the POJO API can also be used to build new process languages.
流程虚拟机(Process Virtual Machine )API暴露流程虚拟机的POJO核心。为了理解Activiti的内部工作机制的教育目的,阅读并把玩API是有趣的。 并且POJO API也能用来构建新的流程语言。
For example:
例如:
PvmProcessDefinition processDefinition = new ProcessDefinitionBuilder()
.createActivity("a")
.initial()
.behavior(new WaitState())
.transition("b")
.endActivity()
.createActivity("b")
.behavior(new WaitState())
.transition("c")
.endActivity()
.createActivity("c")
.behavior(new WaitState())
.endActivity()
.buildProcessDefinition();
PvmProcessInstance processInstance = processDefinition.createProcessInstance();
processInstance.start();
PvmExecution activityInstance = processInstance.findExecution("a");
assertNotNull(activityInstance);
activityInstance.signal(null, null);
activityInstance = processInstance.findExecution("b");
assertNotNull(activityInstance);
activityInstance.signal(null, null);
activityInstance = processInstance.findExecution("c");
assertNotNull(activityInstance);
Expressions(表达式)
Activiti uses UEL for expression-resolving. UEL stands for Unified Expression Language and is part of the EE6 specification ()see the EE6 specification for detailed information). To support all features of latest UEL spec on ALL environements, we use a modified version of JUEL.
Activiti使用UEL进行表达式解析。UEL代表统一表达式(Unified Expression Language ),它是JAVA EE 6规范( the EE6 specification )的一部分。为了在所有环境支持最新的UEL规范,我们采用了JUEL的修改版本。
Expressions can be used in for example Java Service tasks, Execution Listeners, Task Listeners and Conditional sequence flows. Although there are 2 types of expressions, value-expression and method-expression, activiti makes abstraction of this and they can both be used where an expression
is needed.
表达式能够在下列示例中使用: Java Service tasks, Execution Listeners, Task Listeners 和 Conditional sequence flows。尽管存在两种表达式:值表达式和方法表达式。但是,Activiti对此进行了抽象,在需要表达式的地方两者均可使用。
-
Value expression: resolves to a value. By default, all process variables are available to use. Also all spring-beans (if using Spring) are available to use in expressions. On top of that, the DelegateExecution
is also available in the expression-context and can be accessed using the name execution
. Since the execution is exposed as execution
, all variables and spring-beans with name execution
are hidden and cannot be used in an expression. Some examples:
值表达式(Value expression): 解析为一个值。缺省地,所有的流程均可使用。表达式里也可使用所有的spring bean(如果使用Spring)。在此之上,在表达式上下文里也可获得DelegateExecution。因为,执行暴露为execution
,带有名称execution
的所有变量和spring-beans将被隐藏,并且不能在表达式里使用。例如:
${myVar}
${myBean.myProperty}
-
Method expression: invokes a method, with or without parameters. When invoking a method without parameters, be sure to add empty parentheses after the method-name. The passed parameters can be literal values or expressions that are resolved themselves. Examples:
方法表达式:调用一个方法,带不带参数均可。当 调用没有参数的方法,确保在方法名之后加入空括号。这些传入的参数可以字面值或者被解析解释的表达式。例如:
Method expression:
方法表达式:
${printer.print()}
${myBean.addNewOrder('orderName')}
${myBean.doSomething(myVar, execution)}
Note that these expressions support resolving primitives (incl. comparing them), beans, lists, arrays and maps.
注意这些表达式支持解析原型(包括对它们进行比较),beans,列表,数组和map.
For more concrete usage and examples, check out Expressions in Spring, Java Service tasks, Execution Listeners, Task Listeners or Conditional sequence flows.
为了更多的具体的用法和示例,请浏览 Expressions in Spring,Java Service tasks,Execution Listeners, Task Listeners 或者Conditional sequence flows。