实现Activiti动态调整流程(自由跳转、前进、后退、分裂、前加签、后加签等)

原文链接:http://www.cnblogs.com/bluejoe/p/5115888.html

最近对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"}

转载于:https://www.cnblogs.com/bluejoe/p/5115888.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值