盘古BPM体验地址    盘古BPM交流群盘古BPM交流群号:963222735

activiti ActivityImpl类中的坑

Activiti 分享牛 15382℃


摘要:

    前面的activiti系列课程中,我们详细讲解了自由流程的实现,其中自由流程中需要的节点肯定是我们手动构建的,这些节点在流程模板中是没有定义的,因此虽然满足了动态流程的需求,但是存在如下几个问题:

  1. 流程模板即流程文档是一个,我们增加的节点是通过创建ActivityImpl实现的并与流程实例绑定的,并最终添加到流程虚拟机(PVM)中的,这就势必会造成当流程实例完成之后,动态增加的ActivityImpl对象还是存在于流程虚拟机中。

  2. 随着同一个流程模板中对应的流程实例的增加以及动态增加的节点增长,势必会导致流程虚拟机中ActivityImpl集合的扩充,甚至直接内存溢出。

  3. 流程的图片问题(activiti自定义图片生成实现

 这个时候,我们该怎么办呢?思路如下:

当流程实例结束的时候,需要将该流程实例下所有新增的ActivityImpl对象从虚拟机集合中剔除。

当引擎重启的时候,不要将数据库中持久化的流程实例新增的节点数据再次拼接到流程虚拟机(源码分析系列课程中有该过程的详细讲解)。

接下来,我们看一下源码以及问题的解决方案?

ScopeImpl类的核心方法如下:

protected List<ActivityImpl> activities = new ArrayList<ActivityImpl>();
protected Map<String, ActivityImpl> namedActivities = new HashMap<String, ActivityImpl>();
public ActivityImpl findActivity(String activityId) {
ActivityImpl localActivity = namedActivities.get(activityId);
if (localActivity!=null) {
return localActivity;
}
for (ActivityImpl activity: activities) {
ActivityImpl nestedActivity = activity.findActivity(activityId);
if (nestedActivity!=null) {
return nestedActivity;
}
}
return null;
}
public ActivityImpl createActivity(String activityId) {
ActivityImpl activity = new ActivityImpl(activityId, processDefinition);
if (activityId!=null) {
if (processDefinition.findActivity(activityId) != null) {
throw new PvmException("duplicate activity id '" + activityId + "'");
}
namedActivities.put(activityId, activity);
}
activity.setParent(this);
activities.add(activity);
return  activity;
}
public List<ActivityImpl> getActivities() {
return activities;
}

ScopeImpl类为ProcessDefinitionEntity类的父类,这一点需要注意。

在上述的几个方法中,其中createActivity方法创建ActivityImpl 实例对象,并将创建之后的ActivityImpl 对象添加到namedActivities以及activities集合。

findActivity方法:首先根据activityId从namedActivities 集合中获取,如果存在直接查找并返回,否则从activities集合中查找。

这个时候就有问题了?换言之namedActivities集合中存在值就不会从activities集合中查找。但是namedActivities属性并没有getters和setters方法,并且是protected保护级别,完犊子了。它的查找优先级最高,我们没有办法直接获取以及设置值,怎么办么?提供如下两种思路:

  • 自定义一个类并继承ProcessDefinitionEntity类,然后定义相应的namedActivities集合的getters和setters方法方法,如下所示:

public Map<String, ActivityImpl> getNamedActivities() {

    return namedActivities;

    }

但是该方式过于复杂,修改面比较大,优点就是灵活性高,关于该方式的实现,我们会在activiti系列课程中进行详细讲解?可能要一个小时的讲解。

  • 第二种方案:通过反射来获取并操作,缺点:性能不好,并发的时候可能出问题。

具体代码如下:

  public static void removeTemporaryActivityImpl(List<String> tempIds,
   	String processDefinitionId, ProcessEngine processEngine) {
   	if (log.isInfoEnabled()) {
   	log.info("ActivityImplUtils.removeTemporaryActivityImpl================begin");
   	}
   ProcessDefinition processDefinition = processEngine.getRepositoryService()
   	.getProcessDefinition(processDefinitionId);
   	if (processDefinition != null) {
   	ProcessDefinitionEntity pdf = (ProcessDefinitionEntity) processDefinition;
   	Iterator<ActivityImpl> activities = pdf.getActivities().iterator();
   	if (log.isInfoEnabled()) {
   	log.info("ActivityImplUtils.removeTemporaryActivityImpl================1.1 activities:"
   	+ activities);
   	}
   	while (activities.hasNext()) {
   	ActivityImpl activityImpl = activities.next();
   	if (tempIds.contains(activityImpl.getId())) {
   	activities.remove();
   	}
   	}
   	  	if (log.isInfoEnabled()) {
   	log.info("ActivityImplUtils.removeTemporaryActivityImpl================1.2 finally activities:"
   	+ activities);
   	}
   	Class<?> cls = pdf.getClass().getSuperclass().getSuperclass();
   	try {
   	Field declaredField = cls.getDeclaredField("namedActivities");
   	declaredField.setAccessible(true);
   	Map<String, ActivityImpl> namedActivities =(Map<String, ActivityImpl>) declaredField.get(pdf);
   	for (String tempId : tempIds) {
   	namedActivities.remove(tempId);
   	}
   	declaredField.setAccessible(true);
   	declaredField.set(pdf, namedActivities);
   	if (log.isInfoEnabled()) {
   	log.info("ActivityImplUtils.removeTemporaryActivityImpl================1.3 namedActivities:"
   	+ activities);
   	}
   	  	ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
   	processEngineConfiguration.getDeploymentManager().getProcessDefinitionCache()
   	.add(processDefinitionId, pdf);
   	} catch (NoSuchFieldException e) {
   	e.printStackTrace();
   	} catch (Exception e) {
   	e.printStackTrace();
   	}
   	}
   	}

这个类在哪里调用呢?当然是流程实例结束或者删除的时候进行操作,我们可以通过事件监听器进行实现,代码如下:

<bean id="processEngineConfiguration"
   	class="cn.huimin.process.processengine.ProcessEngineConfigurationEx">
   	<property name="dataSource" ref="dataSourceActiviti" />
   	<property name="activityBehaviorFactory" ref="shareniuActivity" />
   	<property name="transactionManager" ref="transactionManagerActiviti" />
   	<property name="idGenerator" ref="uuidGenerator"></property>
   	<property name="databaseSchemaUpdate" value="true" />
   	<property name="jobExecutorActivate" value="false" />
   	<property name="history" value="full"></property>
   	<property name="customDefaultBpmnParseHandlers">
   	<list>
   	<bean class="cn.huimin.process.core.HmUserTaskParseHandler"></bean>
   	</list>
   	</property>
   	<property name="activityFontName" value="宋体" />
   	<property name="labelFontName" value="宋体" />
   	 <property name="typedEventListeners">
   	<map>
   	<entry key="PROCESS_COMPLETED">
   	<bean id="processFinishHandle" class="cn.huimin.process.listener.ProcessFinishHandler">
   	</bean>
   	</entry>
   	</map>
   	</property>
</bean>

     

ProcessFinishHandler类的实现如下:

public class ProcessFinishHandler implements ActivitiEventListener {
private  final static Logger logger = LoggerFactory.getLogger(ProcessFinishHandler.class);
@Override
public void onEvent(ActivitiEvent event) {
//调用上述的工具类即可
}
@Override
public boolean isFailOnException() {
return false;
}
}


ok,大功告成。


转载请注明:分享牛 » activiti ActivityImpl类中的坑