1. 程式人生 > >優雅的實現Activiti動態調整流程(自由跳轉、前進、後退、分裂、前加簽、後加籤等),含範例程式碼!

優雅的實現Activiti動態調整流程(自由跳轉、前進、後退、分裂、前加簽、後加籤等),含範例程式碼!

最近對Activiti做了一些深入的研究,對Activiti的流程機制有了些理解,對動態調整流程也有了一些實踐方法。

現在好好總結一下,一來是對這段時間自己辛苦探索的一個記錄,二來也是為後來者指指路~~~

如下內容準備採用QA的方式寫,很多問題都是當初自己極疑惑的問題,希望能為大家解惑!

Q:可以動態調整流程嗎?

A:可以!可以動態更改流程指向,或者建立新的節點,等等。。。

Q: 更改流程還需要注意什麼?

A: 必須要實現持久化!否則一旦應用重啟,你的流程就犯糊塗了!譬如,你建立了一個新節點,但由於沒有持久化,重啟之後流程引擎找不到那個新節點了。。。

Q: 如何做到優雅?

A: 除了持久化之外,還記住儘量不要因為臨時調整直接更改現有活動(沒準這個活動後面還要照常使用呢!),這種情況可以考慮克隆。第三,不要直接操作資料庫,或者SqlSession,記住自己寫Command!參見我前面的

另外一篇文章。如下程式碼示出執行某個activity後續流程的Cmd:

public class CreateAndTakeTransitionCmd implements Command<java.lang.Void>
{
	private ActivityImpl _activity;

	private String _executionId;

	public CreateAndTakeTransitionCmd(String executionId, ActivityImpl activity)
	{
		_executionId = executionId;
		_activity = activity;
	}

	@Override
	public Void execute(CommandContext commandContext)
	{
		Logger.getLogger(TaskFlowControlService.class)
				.debug(String.format("executing activity: %s", _activity.getId()));

		ExecutionEntity execution = commandContext.getExecutionEntityManager().findExecutionById(_executionId);
		execution.setActivity(_activity);
		execution.performOperation(AtomicOperation.TRANSITION_CREATE_SCOPE);

		return null;
	}
}

Q: 如何新建一個活動?

A: 新建活動可以呼叫processDefinition.createActivity(newActivityId),我們往往可以以某個活動物件為模板來克隆一個新的活動,克隆的方法是分別拷貝各個欄位的值:
	protected ActivityImpl cloneActivity(ProcessDefinitionEntity processDefinition, ActivityImpl prototypeActivity,
			String newActivityId, String... fieldNames)
	{
		ActivityImpl clone = processDefinition.createActivity(newActivityId);
		CloneUtils.copyFields(prototypeActivity, clone, fieldNames);

		return clone;
	}
拷貝欄位的程式碼如下:
import org.apache.commons.lang.reflect.FieldUtils;
import org.apache.log4j.Logger;
import org.junit.Assert;

public abstract class CloneUtils
{
	public static void copyFields(Object source, Object target, String... fieldNames)
	{
		Assert.assertNotNull(source);
		Assert.assertNotNull(target);
		Assert.assertSame(source.getClass(), target.getClass());

		for (String fieldName : fieldNames)
		{
			try
			{
				Field field = FieldUtils.getField(source.getClass(), fieldName, true);
				field.setAccessible(true);
				field.set(target, field.get(source));
			}
			catch (Exception e)
			{
				Logger.getLogger(CloneUtils.class).warn(e.getMessage());
			}
		}
	}
}

一個示例的用法是:
		ActivityImpl clone = cloneActivity(processDefinition, prototypeActivity, cloneActivityId, "executionListeners",
			"properties");

這個語句的意思是克隆prototypeActivity物件的executionListeners和properties欄位。

Q: 如何實現新建活動的持久化?

A: 一個辦法是將新建活動的型別、活動ID(activityId)、incomingTransitions、outgoingTransitions等資訊儲存起來,然後在ProcessEngine啟動的時候,在ProcessDefinition中註冊這些活動。

但還有一種更好的辦法,即只持久化“活動工廠”的資訊。譬如,我們根據step2活動建立一個step21活動,所有的資訊都一樣,這個時候只要持久化工廠型別(活動克隆)、模板活動ID(step2)、新活動ID(step21),這種方法是極其節省空間的,而且簡化了程式碼。比較複雜的例子,是將某個活動分裂成N個序列的會籤活動,這種情況只需要記錄模板活動ID、新活動ID陣列就可以了,不需要記錄更多的資訊。如下示出一個建立N個使用者任務活動的例子:

public class ChainedActivitiesCreator extends RuntimeActivityCreatorSupport implements RuntimeActivityCreator
{
	@Override
	public ActivityImpl[] createActivities(ProcessEngine processEngine, ProcessDefinitionEntity processDefinition,
			RuntimeActivityDefinition info)
	{
		info.setFactoryName(ChainedActivitiesCreator.class.getName());

		if (info.getCloneActivityIds() == null)
		{
			info.setCloneActivityIds(CollectionUtils.arrayToList(new String[info.getAssignees().size()]));
		}

		return createActivities(processEngine, processDefinition, info.getProcessInstanceId(),
			info.getPrototypeActivityId(), info.getNextActivityId(), info.getAssignees(), info.getCloneActivityIds());
	}

	private ActivityImpl[] createActivities(ProcessEngine processEngine, ProcessDefinitionEntity processDefinition,
			String processInstanceId, String prototypeActivityId, String nextActivityId, List<String> assignees,
			List<String> activityIds)
	{
		ActivityImpl prototypeActivity = ProcessDefinitionUtils.getActivity(processEngine, processDefinition.getId(),
			prototypeActivityId);

		List<ActivityImpl> activities = new ArrayList<ActivityImpl>();
		for (int i = 0; i < assignees.size(); i++)
		{
			if (activityIds.get(i) == null)
			{
				String activityId = createUniqueActivityId(processInstanceId, prototypeActivityId);
				activityIds.set(i, activityId);
			}

			ActivityImpl clone = createActivity(processEngine, processDefinition, prototypeActivity,
				activityIds.get(i), assignees.get(i));
			activities.add(clone);
		}

		ActivityImpl nextActivity = ProcessDefinitionUtils.getActivity(processEngine, processDefinition.getId(),
			nextActivityId);
		createActivityChain(activities, nextActivity);

		return activities.toArray(new ActivityImpl[0]);
	}
}

這裡,RuntimeActivityDefinition代表一個工廠資訊,為了方便,不同工廠的個性化資訊存成了一個JSON字串,並會在載入的時候解析成一個Map:

public class RuntimeActivityDefinition
{
	String _factoryName;

	String _processDefinitionId;

	String _processInstanceId;

	Map<String, Object> _properties = new HashMap<String, Object>();

	String _propertiesText;

	public void deserializeProperties() throws IOException
	{
		ObjectMapper objectMapper = new ObjectMapper();
		_properties = objectMapper.readValue(_propertiesText, Map.class);
	}

	public List<String> getAssignees()
	{
		return getProperty("assignees");
	}

	public String getCloneActivityId()
	{
		return getProperty("cloneActivityId");
	}
	//...
}

一個節點分裂的工廠屬性:
{"sequential":true,"assignees":["bluejoe","alex"],"cloneActivityId":"2520001:step2:1419823449424-8","prototypeActivityId":"step2"}

相關推薦

優雅實現Activiti動態調整流程自由前進後退分裂範例程式碼

最近對Activiti做了一些深入的研究,對Activiti的流程機制有了些理解,對動態調整流程也有了一些實踐方法。現在好好總結一下,一來是對這段時間自己辛苦探索的一個記錄,二來也是為後來者指指路~~~如下內容準備採用QA的方式寫,很多問題都是當初自己極疑惑的問題,希望能為大

擴充套件Activiti-5.12輕鬆實現流程節點間自由和任意駁回/撤回

由於專案需要,最近對開源工作流引擎Activiti-5.12的功能做了一下擴充套件,實現了以下功能: 1.自由流(流程節點間自由跳轉和任意駁回/撤回) 2.流程會籤任務串並行模式切換 一、自由流在已有流程模型的的基礎上,每個流程例項當前任務可以任意駁回/撤回或者向後續節點任

ligerGrid實現無縫動態滾動行生成新/刪除舊

兩種方案: 一種是從上往下滾動: //addData是一行新資料,如{"id":1,"name":"abc"} //manager.getRow(0)是第一行 manager.addRow(addData,manager.getRow(0),true);//在頂部增加行 var l

如何實現動態調整隱藏head…

需求:根據某種需要,可能需要動態調整listview的頁首頁尾,譬如將header作為顯示板使用。 難點:listView.addHeaderView()方法必須在setAdapter()方法前呼叫,否則就會拋異常。至於為什麼會拋異常,檢視下ListView的原始碼即可發現。因此,在設定HeaderView

演算法(第四版)學習筆記之java實現能夠動態調整陣列大小的棧

下壓(LIFO)棧:能夠動態調整陣列大小的實現 import java.util.Iterator; public class ResizingArrayStack<Item> impl

實現Android 動態載入APKFragment or Activity實現

最近由於專案太大了,導致編譯通不過(Android對一個應用中的方法個數貌似有限制),所以一直琢磨著能否將某些模組的APK不用安裝,動態載入,通過在網上查詢資料和網友的幫助,終於實現了APK的動態載入,網路上介紹APK動態載入的文章非常多,但是我覺得寫得非常好的就是這位大牛

phpcms實現手機端與PC端雙模板的方法與詳細教程同步

<script type="text/javascript"> function browserRedirect() { var sUserAgent = navigator.userAgent.toLowerCase(); var bIsIpad = sUserAgent.match(/ipad

Android開發25--framebyframe幀動畫並實現啟動介面到主介面的

      Drawable animation可以載入Drawable資源實現幀動畫。AnimationDrawable是實現Drawable animations的基本類。推薦用XML檔案的方法實現Drawable動畫,不推薦在程式碼中實現。這種XML檔案存放在工程中r

Web前端:如何實現選擇select下拉框選中其他頁面

option chang 前端 實現 cati onchange 下拉框選中 b- class <select onchange="window.location=this.value;"><option value="a.html">用戶管理<

教PHP程序員如何找單位全職+實習超有用啊

幫助 每日 補充 高校 con 見習 ech dex 自動 教PHP程序員如何找單位(全職+實習),超有用啊! 現在很多企業不招剛畢業大學生,原因就是剛畢業大學生沒工作經驗,有了這些,你會很有經驗!首先,確定你的行業,和最關註的企業,把自己的簡歷放到人家的官方網站上,然後定

各種頁面定時倒計時代碼總結

倒數 自動跳轉 str ani import rect ext document color 一、使用setTimeout函數實現定時跳轉(如下代碼要寫在body區域內) <script type="text/javascript"> //3秒鐘之後跳

Emergency只有10分儲存一下程式碼。求大神改正。

#include <stdio.h> #include <stdlib.h> typedef struct LinkedNode *List; typedef int DistType; typedef int Vertex; typede

正則表示式詳解貪婪與懶惰前瞻與向引用

之前嫌正則麻煩,一直沒有深入去了解過正則,能不用的地方就不使用。 最近專案中遇到了不可避免的正則使用,所以花了點時間去了解並整理了一下,理解不一定完全準確,如有不對歡迎指出,希望對大家有所幫助。 一、名詞解釋 首先我們瞭解幾個名詞:元字元 、 普通字元、列印字元、非列印字元、 限定符 、定位符、非列

Java 004 控制流程語句 控制語句

知識點梳理 心得體會 小知識點 1.if語句可以得到結果或得到輸出,而三元運算子只能得到結果 2.if可判斷boolean、範圍、常量,switch只能判斷有限的常量 3.for迴圈誤將其他程式碼放入迴圈體大括號內可能會出現死迴圈 4.for迴圈、while迴圈初始化表示式

spring security實現登入驗證以及根據使用者身份不同頁面

想關依賴,採用session加redis儲存使用者資訊 <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security

吳恩達機器學習筆記作業及附加題答案連結

吳恩達機器學習筆記(一) 標籤(空格分隔): 機器學習 吳恩達機器學習筆記一 一機器學習簡介 機器學習的定義 監督學習 非監督學習

專案部署之nginx實現PC端和移動端自動

假設PC端域名為 www.abc.com     移動端域名為m.abc.com PC端nginx配置檔案server中加入如下程式碼: if ($http_host !~ "^www.abc.com$") { rewrite ^(.*) http://ww

R語言實戰--隨機產生服從不同分佈函式的資料正態分佈泊松分佈並將資料寫入資料框儲存到硬碟

隨機產生服從不同分佈的資料 均勻分佈——runif() > x1=round(runif(100,min=80,max=100)) > x1 [1] 93 100 98 98 92 98 98 89 90 98 100 89

IBM 收購 RedHat紅帽340 億美元

IBM今天宣佈了以高達340億美元的價格收購Red Hat,這筆交易在開源軟體行業和雲端計算行業勢必都會引起地震。 Red Hat是將開源軟體商業化的先驅之一,其採用的模式是,程式碼免費提供給其他公司使用,以便重新分發和修改。開源軟體已逐漸成為許多應用系統的基石,即便在大企業中也是如此,比如Lin

Qt隨筆記視窗

最近在用Qt寫一些東西,以前沒接觸過Qt。所以記下一些遇到的問題,以防以後會用。 這次遇到的問題主要是兩個視窗之間的跳轉問題。相信如果用過Qt開發的朋友們一開始都會遇到這個問題。去網上找對應的解決方法時也會有很多。主要是訊號與槽的方法。 但是我用了訊號與槽,也並