Chapter 5. API
Table of Contents
- Engine API(引擎API )
- Exception strategy(異常處理策略)
- Unit testing(單元測試)
- The process engine in a webapplication(在Web應(yīng)用里的流程引擎)
- 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章節(jié)里描述的那樣,它可以用幾種方法建立。從 ProcessEngine
里能夠獲得包括工作流/BPM方法的各種各樣的服務(wù)。 因為ProcessEngine
和服務(wù)對象是線程安全的,所以能夠為整個服務(wù)器范圍保留這些服務(wù)的引用為1。
![api.services[5] api.services[5]](http://www.tkk7.com/images/blogjava_net/lewhwa/Windows-Live-Writer/7b1a493cfcdf_13FC/api.services%5B5%5D_thumb.png)
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.
服務(wù)名是自解釋的,相當(dāng)明了。要了解服務(wù)和引擎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都可拋出這個異常,但是 某些方法產(chǎn)生'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.
在上例,當(dāng)傳遞一個不存在的任務(wù)的id,將會拋出一個異常。當(dāng)然,javadoc 明明白白說明taskId不能為null,當(dāng)傳遞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:
當(dāng)Activiti引擎發(fā)現(xiàn)數(shù)據(jù)庫結(jié)構(gòu)版本和引擎版本之間不匹配時,將拋出這個異常。
-
ActivitiOptimisticLockingException:
Thrown when an optimistic locking occurs in the datastore caused by concurrent access of the same data entry.
ActivitiOptimisticLockingException:
當(dāng)數(shù)據(jù)庫里由對相同的數(shù)據(jù)項并發(fā)訪問導(dǎo)致樂觀鎖定時發(fā)生。
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.
業(yè)務(wù)流程是軟件項目的一個集成部分,那么應(yīng)當(dāng)采用正常應(yīng)用邏輯測試 :采用單元測試。因為Activiti是一個嵌入式Java引擎,所以編寫業(yè)務(wù)流程的單元測試像編寫正常的單元測試一樣簡單。
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風(fēng)格。在Junit 3風(fēng)格里,必須擴展org.activiti.engine.test.ActivitiTestCase。這將使得通過保護成員字段獲得流程引擎和服務(wù)。在測試的setup()里,缺省地流程引擎將通過classpath上的 activiti.cfg.xml 資源初始化流程引擎。為了指定不同的 配置文件,覆蓋 getConfigurationResource() 方法。當(dāng)配置資源是相同時,流程引擎靜態(tài)地緩存多個單元測試。
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作為測試類。在測試結(jié)束,將刪除包括所有相關(guān)的流程實例,任務(wù)等等部署。Deployment 標注也支持顯式設(shè)置資源的位置。詳情參見 the Javadocs。
Taking all that in account, a Junit 3 style test looks as follows.
將上面所有的情況考慮進去,Junit 3風(fēng)格的測試方式如下所示。
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.
當(dāng)使用Junit 4風(fēng)格編寫單元測試時,為了獲得相同的功能,必須使用 org.activiti.engine.test.ActivitiRule 規(guī)則。采用這個規(guī)則,通過getter獲得流程引擎和服務(wù)。采用 ActivitiTestCase(如上),包括使用 org.activiti.engine.test.Deployment (它的使用和配置如上)標注的這個規(guī)則,它將在classpath上尋找缺省的配置。當(dāng)使用相同配置資源時,流程引擎靜態(tài)地緩存多個單元測試。
Following code snippet shows an example of using the Junit 4 style of testing and the usage of the ActivitiRule.
下面代碼片段展示了采用的Junit 4風(fēng)格的測試和 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應(yīng)用里的流程引擎)
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應(yīng)用里面,這意味著當(dāng)容器引導(dǎo)和關(guān)閉引擎時,可能建立這個流程引擎。
The following code snippet how this is done in a regular Servlet environment, using a ServletContextListener:
下列代碼通過使用 ServletContextListener,展示了在一個普通的 Servlet 環(huán)境下如何完成這個工作的。
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上具有多個如此的資源文件,確保它們都具有不同的名字。當(dāng)需要流程引擎時,它將采用下面的方法來取得
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.
當(dāng)然,采用建立流程引擎的變體也是可能的。如 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()
。那將正常關(guān)閉所有以初始化的流程引擎。
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的內(nèi)部工作機制的教育目的,閱讀并把玩API是有趣的。 并且POJO API也能用來構(gòu)建新的流程語言。
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代表統(tǒng)一表達式(Unified Expression Language ),它是JAVA EE 6規(guī)范( the EE6 specification )的一部分。為了在所有環(huán)境支持最新的UEL規(guī)范,我們采用了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。因為,執(zhí)行暴露為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:
方法表達式:調(diào)用一個方法,帶不帶參數(shù)均可。當(dāng) 調(diào)用沒有參數(shù)的方法,確保在方法名之后加入空括號。這些傳入的參數(shù)可以字面值或者被解析解釋的表達式。例如:
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,列表,數(shù)組和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。