ALF-8144 Added in caching of QName mappings, TaskInstances, Task properties and ProcessContext variables. Effectively merged fixes from V3.4 revisions 25061,25072,25261 and 25326.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@26973 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
N Smith
2011-04-19 13:26:38 +00:00
parent 74d754b5ac
commit 411dccf795
6 changed files with 659 additions and 126 deletions

View File

@@ -135,6 +135,8 @@
<value>org/jbpm/taskmgmt/log/SwimlaneCreateLog.hbm.xml</value>
<value>org/jbpm/taskmgmt/log/SwimlaneAssignLog.hbm.xml</value>
<value>org/jbpm/job/CleanUpProcessJob.hbm.xml</value>
<value>org/alfresco/repo/workflow/jbpm/jbpm.ext.queries.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties" ref="hibernateConfigProperties" />

View File

@@ -24,6 +24,7 @@ import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.workflow.WorkflowException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QNameCache;
/**
* @since 3.4.e
@@ -38,7 +39,7 @@ public abstract class AlfrescoBpmEngine extends BPMEngine
protected DictionaryService dictionaryService;
protected WorkflowObjectFactory factory;
protected WorkflowAuthorityManager authorityManager;
/**
* {@inheritDoc}
*/

View File

@@ -381,4 +381,9 @@ public class WorkflowObjectFactory
{
return qNameConverter.mapNameToQName(name);
}
public void clearQNameCache()
{
qNameConverter.clearCache();
}
}

View File

@@ -22,6 +22,7 @@ package org.alfresco.repo.workflow;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.QNameCache;
/**
* @since 3.4.e
@@ -30,6 +31,8 @@ import org.alfresco.service.namespace.QName;
*/
public class WorkflowQNameConverter
{
private static final int MAX_QNAME_CACHE_SIZE = 5000;
private final QNameCache cache = new QNameCache(MAX_QNAME_CACHE_SIZE);
private final NamespacePrefixResolver prefixResolver;
public WorkflowQNameConverter(NamespacePrefixResolver prefixResolver)
@@ -40,21 +43,21 @@ public class WorkflowQNameConverter
/**
* Map QName to jBPM variable name
*
* @param name QName
* @param qName QName
* @return jBPM variable name
*/
public String mapQNameToName(QName name)
public String mapQNameToName(QName qName)
{
// NOTE: Map names using old conversion scheme (i.e. : -> _) as well as new scheme (i.e. } -> _)
// NOTE: Use new scheme
String nameStr = name.toPrefixString(prefixResolver);
if (nameStr.indexOf('_') != -1 && nameStr.indexOf('_') < nameStr.indexOf(':'))
String name = cache.getName(qName);
if(name == null)
{
return nameStr.replace(':', '}');
name = convertQNameToName(qName);
cache.putQNameToName(qName, name);
cache.putNameToQName(name, qName);
}
return nameStr.replace(':', '_');
return name;
}
/**
* Map QName to jBPM variable name
*
@@ -62,6 +65,23 @@ public class WorkflowQNameConverter
* @return jBPM variable name
*/
public QName mapNameToQName(String name)
{
QName qName = cache.getQName(name);
if (qName == null)
{
qName = convertNameToQName(name);
cache.putNameToQName(name, qName);
cache.putQNameToName(qName, name);
}
return qName;
}
public void clearCache()
{
cache.clear();
}
private QName convertNameToQName(String name)
{
if(name.indexOf(QName.NAMESPACE_BEGIN)==0)
{
@@ -78,5 +98,19 @@ public class WorkflowQNameConverter
}
return QName.createQName(qName, prefixResolver);
}
private String convertQNameToName(QName name)
{
// NOTE: Map names using old conversion scheme (i.e. : -> _) as well as new scheme (i.e. } -> _)
// NOTE: Use new scheme
String nameStr = name.toPrefixString(prefixResolver);
if (nameStr.indexOf('_') != -1 && nameStr.indexOf('_') < nameStr.indexOf(':'))
{
// Return full QName string.
return name.toString();
}
// Return prefixed QName string.
return nameStr.replace(':', '_');
}
}

View File

@@ -66,11 +66,12 @@ import org.alfresco.service.cmr.workflow.WorkflowTaskQuery;
import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.cmr.workflow.WorkflowTimer;
import org.alfresco.service.cmr.workflow.WorkflowTransition;
import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
import org.hibernate.CacheMode;
import org.hibernate.Criteria;
import org.hibernate.FlushMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Conjunction;
@@ -125,7 +126,7 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
protected AuthorityDAO authorityDAO;
protected JbpmTemplate jbpmTemplate;
protected SearchService unprotectedSearchService;
// Company Home
protected StoreRef companyHomeStore;
protected String companyHomePath;
@@ -738,7 +739,7 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
Serializable packageNode = parameters.get(WorkflowModel.ASSOC_PACKAGE);
if (packageNode != null)
{
String pckgName = mapQNameToName(WorkflowModel.ASSOC_PACKAGE);
String pckgName = factory.mapQNameToName(WorkflowModel.ASSOC_PACKAGE);
processContext.setVariable(pckgName, new JBPMNode((NodeRef) packageNode, serviceRegistry));
}
}
@@ -793,7 +794,6 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.WorkflowComponent#getActiveWorkflows(java.lang.String)
*/
@SuppressWarnings("unchecked")
public List<WorkflowInstance> getActiveWorkflows(final String workflowDefinitionId)
{
return getWorkflowsInternal(workflowDefinitionId, true);
@@ -992,7 +992,7 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
for (Map.Entry<String, Object> entry : tokenVars.entrySet())
{
String key = entry.getKey();
QName qname = mapNameToQName(key);
QName qname = factory.mapNameToQName(key);
if (!properties.containsKey(key))
{
@@ -1312,46 +1312,15 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
List<TaskInstance> tasks;
if (state.equals(WorkflowTaskState.IN_PROGRESS))
{
TaskMgmtSession taskSession = context.getTaskMgmtSession();
tasks = taskSession.findTaskInstances(authority);
return findActiveTaskInstances(authority, context);
}
else
{
// Note: This method is not implemented by jBPM
tasks = findCompletedTaskInstances(context, authority);
return getWorkflowTasks(tasks);
}
return getWorkflowTasks(tasks);
}
/**
* Gets the completed task list for the specified actor
*
* TODO: This method provides a query that's not in JBPM! Look
* to have JBPM implement this.
*
* @param jbpmContext
* the jbpm context
* @param actorId
* the actor to retrieve tasks for
* @return the tasks
*/
private List<TaskInstance> findCompletedTaskInstances(JbpmContext jbpmContext, String actorId)
{
List<TaskInstance> result = null;
try
{
Session session = jbpmContext.getSession();
Query query = session.createQuery(COMPLETED_TASKS_QUERY);
query.setString("actorId", actorId);
result = query.list();
}
catch (Exception e)
{
String msg = messageService.getMessage(ERR_FIND_COMPLETED_TASK_INSTS, actorId);
throw new JbpmException(msg, e);
}
return result;
}
});
}
@@ -1362,8 +1331,207 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.TaskComponent#getPooledTasks(java.util.List)
/**
* Gets the completed task list for the specified actor
*
* @param jbpmContext the jbpm context
* @param actorId the actor to retrieve tasks for
* @return the tasks
*/
@SuppressWarnings("unchecked")
private List<TaskInstance> findCompletedTaskInstances(JbpmContext jbpmContext, String actorId)
{
List<TaskInstance> result = null;
try
{
Session session = jbpmContext.getSession();
Query query = session.createQuery(COMPLETED_TASKS_QUERY);
query.setString("actorId", actorId);
result = query.list();
}
catch (Exception e)
{
String msg = messageService.getMessage(ERR_FIND_COMPLETED_TASK_INSTS, actorId);
throw new JbpmException(msg, e);
}
return result;
}
@SuppressWarnings("unchecked")
private List<WorkflowTask> findActiveTaskInstances(final String authority, JbpmContext context)
{
Session session = context.getSession();
Query query = session.getNamedQuery("org.alfresco.repo.workflow.findTaskInstancesByActorId");
query.setString("actorId", authority);
query.setBoolean("true", true);
List<WorkflowTask> workflowTasks = getWorkflowTasks(session, query.list());
// Do we need to clear a session here? It takes 3 seconds with 2000 workflows.
// session.clear();
return workflowTasks;
}
protected List<WorkflowTask> getWorkflowTasks(Session session, List<Object[]> rows)
{
List<WorkflowTask> workflowTasks = new ArrayList<WorkflowTask>(rows.size());
/// ------------------------
// Preload data into L1 session
List<Long> taskInstanceIds = new ArrayList<Long>(rows.size());
List<Long> contextInstanceIds = new ArrayList<Long>(rows.size());
for (Object[] row : rows)
{
TaskInstance ti = (TaskInstance) row[0];
taskInstanceIds.add(ti.getId());
ContextInstance ci = (ContextInstance) row[8];
contextInstanceIds.add(ci.getId());
}
Map<Long, TaskInstance> taskInstanceCache = new HashMap<Long, TaskInstance>(rows.size());
if (taskInstanceIds.size() > 0)
{
taskInstanceCache = cacheTasks(session, taskInstanceIds);
}
Map<Long, TokenVariableMap> variablesCache = new HashMap<Long, TokenVariableMap>(rows.size());
if (contextInstanceIds.size() > 0)
{
variablesCache = cacheVariables(session, contextInstanceIds);
}
taskInstanceIds.clear();
contextInstanceIds.clear();
/// ------------------------
for(Object[] row : rows)
{
WorkflowTask workflowTask = makeWorkflowTask(row, taskInstanceCache, variablesCache);
if(workflowTask !=null )
{
workflowTasks.add(workflowTask);
}
}
return workflowTasks;
}
private WorkflowTask makeWorkflowTask(Object[] row, Map<Long, TaskInstance> taskInstanceCache, Map<Long, TokenVariableMap> variablesCache)
{
TaskInstance ti = (TaskInstance) row[0];
Token token = (Token)row[2];
ProcessInstance processInstance = (ProcessInstance)row[3];
Node node = (Node)row[4];
Task task = (Task)row[5];
ProcessDefinition processDefinition = (ProcessDefinition)row[6];
Task startTask = (Task)row[7];
ContextInstance contextInstance = (ContextInstance) row[8];
if (tenantService.isEnabled())
{
try
{
tenantService.checkDomain(processDefinition.getName());
}
catch (RuntimeException re)
{
// deliberately skip this one - due to domain mismatch - eg. when querying by group authority
return null;
}
}
// TaskInstance with some precached properties
TaskInstance helperTi = taskInstanceCache.get(ti.getId());
@SuppressWarnings("unchecked")
Map<String, Object> variables = variablesCache.get(contextInstance.getId()).getVariables();
// WorkflowTaskProperies
Map<QName, Serializable> properties = getTaskProperties(helperTi != null ? helperTi : ti, false, variablesCache);
WorkflowDefinition wfDef = createWorkflowDefinition(processDefinition, startTask);
WorkflowInstance instance = createWorkflowInstance(processInstance, wfDef, null, variables);
WorkflowNode wfNode = createWorkflowNode(node);
WorkflowPath path = createWorkflowPath(token, instance, wfNode);
WorkflowTaskDefinition taskDef = createWorkflowTaskDefinition(task);
return createWorkflowTask(ti, taskDef, path, properties);
}
private Map<Long, TokenVariableMap> cacheVariables(Session session, List<Long> ids)
{
// Preload data into L1 session
int batchSize = 800; // Must limit IN clause size!
List<Long> batch = new ArrayList<Long>(ids.size());
Map<Long, TokenVariableMap> cachedResults = new HashMap<Long, TokenVariableMap>();
for (Long id : ids)
{
batch.add(id);
if (batch.size() >= batchSize)
{
cacheVariablesNoBatch(session, batch, cachedResults);
batch.clear();
}
}
if (batch.size() > 0)
{
cacheVariablesNoBatch(session, batch, cachedResults);
}
batch.clear();
return cachedResults;
}
@SuppressWarnings({ "unchecked", "cast" })
private void cacheVariablesNoBatch(Session session, List<Long> contextInstanceIds, Map<Long, TokenVariableMap> variablesCache)
{
Query query = session.getNamedQuery("org.alfresco.repo.workflow.cacheInstanceVariables");
query.setParameterList("ids", contextInstanceIds);
query.setCacheMode(CacheMode.PUT);
query.setFlushMode(FlushMode.MANUAL);
query.setCacheable(true);
List<TokenVariableMap> results = (List<TokenVariableMap>) query.list();
for (TokenVariableMap tokenVariableMap : results)
{
variablesCache.put(tokenVariableMap.getContextInstance().getId(), tokenVariableMap);
}
}
private Map<Long, TaskInstance> cacheTasks(Session session, List<Long> ids)
{
// Preload data into L1 session
int batchSize = 800; // Must limit IN clause size!
List<Long> batch = new ArrayList<Long>(ids.size());
Map<Long, TaskInstance> cachedResults = new HashMap<Long, TaskInstance>();
for (Long id : ids)
{
batch.add(id);
if (batch.size() >= batchSize)
{
cacheTasksNoBatch(session, batch, cachedResults);
batch.clear();
}
}
if (batch.size() > 0)
{
cacheTasksNoBatch(session, batch, cachedResults);
}
batch.clear();
return cachedResults;
}
@SuppressWarnings({ "unchecked", "cast" })
private void cacheTasksNoBatch(Session session, List<Long> taskInstanceIds, Map<Long, TaskInstance> returnMap)
{
Query query = session.getNamedQuery("org.alfresco.repo.workflow.cacheTaskInstanceProperties");
query.setParameterList("ids", taskInstanceIds);
query.setCacheMode(CacheMode.PUT);
query.setFlushMode(FlushMode.MANUAL);
query.setCacheable(true);
List<TaskInstance> results = (List<TaskInstance>) query.list();
for (TaskInstance taskInstance : results)
{
returnMap.put(taskInstance.getId(), taskInstance);
}
}
/*
* (non-Javadoc)
*
* @see
* org.alfresco.repo.workflow.TaskComponent#getPooledTasks(java.util.List)
*/
@SuppressWarnings("unchecked")
public List<WorkflowTask> getPooledTasks(final List<String> authorities)
@@ -1506,7 +1674,7 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
for (Map.Entry<QName, Object> prop : props.entrySet())
{
Conjunction value = Restrictions.conjunction();
value.add(Restrictions.eq("name", mapQNameToName(prop.getKey())));
value.add(Restrictions.eq("name", factory.mapQNameToName(prop.getKey())));
value.add(Restrictions.eq("value", prop.getValue().toString()));
values.add(value);
}
@@ -1535,7 +1703,7 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
for (Map.Entry<QName, Object> prop : props.entrySet())
{
Conjunction value = Restrictions.conjunction();
value.add(Restrictions.eq("name", mapQNameToName(prop.getKey())));
value.add(Restrictions.eq("name", factory.mapQNameToName(prop.getKey())));
value.add(Restrictions.eq("value", prop.getValue().toString()));
values.add(value);
}
@@ -1786,7 +1954,6 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
// create properties to set on task instance
Map<QName, Serializable> newProperties = new HashMap<QName, Serializable>(10);
if(properties!=null)
{
newProperties.putAll(properties);
@@ -2201,11 +2368,23 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
/**
* Gets Properties of Task
*
* @param instance task instance
* @param properties properties to set
* @param instance task instance
* @param properties properties to set
*/
protected Map<QName, Serializable> getTaskProperties(TaskInstance instance, boolean localProperties)
{
return getTaskProperties(instance, localProperties, null);
}
/**
* Gets Properties of Task
*
* @param instance task instance
* @param properties properties to set
* @param variablesCache cahce of context instance variables if any exists
*/
@SuppressWarnings("unchecked")
protected Map<QName, Serializable> getTaskProperties(TaskInstance instance, boolean localProperties)
protected Map<QName, Serializable> getTaskProperties(TaskInstance instance, boolean localProperties, Map<Long, TokenVariableMap> variablesCache)
{
// retrieve type definition for task
TypeDefinition taskDef = getFullTaskDefinition(instance);
@@ -2220,7 +2399,15 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
Token token = instance.getToken();
while (token != null)
{
TokenVariableMap varMap = context.getTokenVariableMap(token);
TokenVariableMap varMap = null;
if (variablesCache != null && variablesCache.containsKey(context.getId()))
{
varMap = variablesCache.get(context.getId());
}
else
{
varMap = context.getTokenVariableMap(token);
}
if (varMap != null)
{
Map<String, Object> tokenVars = varMap.getVariablesLocally();
@@ -2241,7 +2428,7 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
for (Entry<String, Object> entry : vars.entrySet())
{
String key = entry.getKey();
QName qname = mapNameToQName(key);
QName qname = factory.mapNameToQName(key);
// add variable, only if part of task definition or locally defined
// on task
@@ -2272,11 +2459,11 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
}
// map jBPM task instance collections to associations
Set pooledActors = instance.getPooledActors();
Set<PooledActor> pooledActors = instance.getPooledActors();
if (pooledActors != null)
{
List<NodeRef> pooledNodeRefs = new ArrayList<NodeRef>(pooledActors.size());
for (PooledActor pooledActor : (Set<PooledActor>)pooledActors)
for (PooledActor pooledActor : pooledActors)
{
NodeRef pooledNodeRef = null;
String pooledActorId = pooledActor.getActorId();
@@ -2487,7 +2674,7 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
// no specific mapping to jBPM task has been established, so place
// into
// the generic task variable bag
String name = mapQNameToName(key);
String name = factory.mapQNameToName(key);
instance.setVariableLocally(name, value);
}
}
@@ -2531,7 +2718,7 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
if (description == null || description.length() == 0)
{
description = (String) instance.getContextInstance().getVariable(
mapQNameToName(WorkflowModel.PROP_WORKFLOW_DESCRIPTION));
factory.mapQNameToName(WorkflowModel.PROP_WORKFLOW_DESCRIPTION));
if (description != null && description.length() > 0)
{
defaultValues.put(WorkflowModel.PROP_DESCRIPTION, description);
@@ -2562,7 +2749,7 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
if (description == null || description.length() == 0)
{
description = (String) instance.getContextInstance().getVariable(
mapQNameToName(WorkflowModel.PROP_WORKFLOW_DESCRIPTION));
factory.mapQNameToName(WorkflowModel.PROP_WORKFLOW_DESCRIPTION));
if (description != null && description.length() > 0)
{
Map<QName, Serializable> defaultValues = new HashMap<QName, Serializable>();
@@ -2583,30 +2770,30 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
Map<QName, Serializable> taskProperties = getTaskProperties(startTask, true);
ContextInstance processContext = startTask.getContextInstance();
String workflowDescriptionName = mapQNameToName(WorkflowModel.PROP_WORKFLOW_DESCRIPTION);
String workflowDescriptionName = factory.mapQNameToName(WorkflowModel.PROP_WORKFLOW_DESCRIPTION);
if (!processContext.hasVariable(workflowDescriptionName))
{
processContext.setVariable(workflowDescriptionName, taskProperties
.get(WorkflowModel.PROP_WORKFLOW_DESCRIPTION));
}
String workflowDueDateName = mapQNameToName(WorkflowModel.PROP_WORKFLOW_DUE_DATE);
String workflowDueDateName = factory.mapQNameToName(WorkflowModel.PROP_WORKFLOW_DUE_DATE);
if (!processContext.hasVariable(workflowDueDateName))
{
processContext.setVariable(workflowDueDateName, taskProperties.get(WorkflowModel.PROP_WORKFLOW_DUE_DATE));
}
String workflowPriorityName = mapQNameToName(WorkflowModel.PROP_WORKFLOW_PRIORITY);
String workflowPriorityName = factory.mapQNameToName(WorkflowModel.PROP_WORKFLOW_PRIORITY);
if (!processContext.hasVariable(workflowPriorityName))
{
processContext.setVariable(workflowPriorityName, taskProperties.get(WorkflowModel.PROP_WORKFLOW_PRIORITY));
}
String workflowPackageName = mapQNameToName(WorkflowModel.ASSOC_PACKAGE);
String workflowPackageName = factory.mapQNameToName(WorkflowModel.ASSOC_PACKAGE);
if (!processContext.hasVariable(workflowPackageName))
{
Serializable packageNodeRef = taskProperties.get(WorkflowModel.ASSOC_PACKAGE);
processContext.setVariable(workflowPackageName, convertNodeRefs(packageNodeRef instanceof List<?>,
packageNodeRef));
}
String workflowContextName = mapQNameToName(WorkflowModel.PROP_CONTEXT);
String workflowContextName = factory.mapQNameToName(WorkflowModel.PROP_CONTEXT);
if (!processContext.hasVariable(workflowContextName))
{
Serializable contextRef = taskProperties.get(WorkflowModel.PROP_CONTEXT);
@@ -2832,48 +3019,6 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
return name;
}
/**
* Map jBPM variable name to QName
*
* @param name
* jBPM variable name
* @return qname
*/
private QName mapNameToQName(String name)
{
QName qname = null;
// NOTE: Map names using old conversion scheme (i.e. : -> _) as well as new scheme (i.e. } -> _)
String qnameStr = (name.indexOf('}') == -1) ? name.replaceFirst("_", ":") : name.replace("}", ":");
try
{
qname = QName.createQName(qnameStr, this.namespaceService);
}
catch(NamespaceException e)
{
qname = QName.createQName(name, this.namespaceService);
}
return qname;
}
/**
* Map QName to jBPM variable name
*
* @param name
* QName
* @return jBPM variable name
*/
private String mapQNameToName(QName name)
{
// NOTE: Map names using old conversion scheme (i.e. : -> _) as well as new scheme (i.e. } -> _)
// NOTE: Use new scheme
String nameStr = name.toPrefixString(this.namespaceService);
if (nameStr.indexOf('_') != -1 && nameStr.indexOf('_') < nameStr.indexOf(':'))
{
return nameStr.replace(':', '}');
}
return nameStr.replace(':', '_');
}
/**
* Get an I18N Label for a workflow item
*
@@ -2936,14 +3081,32 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
*
* @param token
* JBoss JBPM Token
* @param wfInstance
* @param node
* @return Workflow Path
*/
protected WorkflowPath createWorkflowPath(Token token)
{
String tokenId = token.getFullName().replace("/", WORKFLOW_TOKEN_SEPERATOR);
String id = token.getProcessInstance().getId() + WORKFLOW_PATH_SEPERATOR + tokenId;
if(token == null)
return null;
WorkflowInstance wfInstance = createWorkflowInstance(token.getProcessInstance());
WorkflowNode node = createWorkflowNode(token.getNode());
return createWorkflowPath(token, wfInstance, node);
}
/**
* Creates a Workflow Path
*
* @param token
* JBoss JBPM Token
* @param wfInstance
* @param node
* @return Workflow Path
*/
protected WorkflowPath createWorkflowPath(Token token, WorkflowInstance wfInstance, WorkflowNode node)
{
String tokenId = token.getFullName().replace("/", WORKFLOW_TOKEN_SEPERATOR);
String id = token.getProcessInstance().getId() + WORKFLOW_PATH_SEPERATOR + tokenId;
boolean isActive = !token.hasEnded();
return factory.createPath(id, wfInstance, node, isActive);
}
@@ -2957,6 +3120,8 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
*/
protected WorkflowNode createWorkflowNode(Node node)
{
if(node==null)
return null;
String processName = node.getProcessDefinition().getName();
String name = node.getName();
String type = getRealNode(node).getClass().getSimpleName();
@@ -2989,6 +3154,8 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
*/
protected WorkflowTransition createWorkflowTransition(Transition transition)
{
if(transition==null)
return null;
String id = transition.getName();
Node node = transition.getFrom();
boolean isDefault = node.getDefaultLeavingTransition().equals(transition);
@@ -3014,23 +3181,31 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
return createWorkflowInstance(instance, null);
}
@SuppressWarnings("unchecked")
private WorkflowInstance createWorkflowInstance(ProcessInstance instance, Date endDate)
{
if(instance == null)
return null;
Map<String, Object> variables = instance.getContextInstance().getVariables();
WorkflowDefinition definition = createWorkflowDefinition(instance.getProcessDefinition());
return createWorkflowInstance(instance, definition, endDate, variables);
}
/**
* Creates a Workflow Instance
*
* @param instance
* JBoss JBPM Process Instance
* @param endDate
* @param variables
* @return Workflow instance
*/
@SuppressWarnings("unchecked")
protected WorkflowInstance createWorkflowInstance(ProcessInstance instance, Date endDate)
protected WorkflowInstance createWorkflowInstance(ProcessInstance instance, WorkflowDefinition definition, Date endDate, Map<String, Object> variables)
{
if(instance == null)
return null;
String id = Long.toString(instance.getId());
Map<String, Object> variables = instance.getContextInstance().getVariables();
WorkflowDefinition definition = createWorkflowDefinition(instance.getProcessDefinition());
Date startDate = instance.getStart();
boolean isActive = false;
if (endDate == null)
@@ -3050,41 +3225,66 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
*/
protected WorkflowDefinition createWorkflowDefinition(ProcessDefinition definition)
{
if(definition==null)
return null;
Task startTask = definition.getTaskMgmtDefinition().getStartTask();
return createWorkflowDefinition(definition, startTask);
}
/**
* Creates a Workflow Definition
*
* @param definition
* JBoss Process Definition
* @return Workflow Definition
*/
private WorkflowDefinition createWorkflowDefinition(ProcessDefinition definition, Task startTask)
{
if(definition==null)
return null;
String id = Long.toString(definition.getId());
String name = definition.getName();
int version = definition.getVersion();
Task startTask = definition.getTaskMgmtDefinition().getStartTask();
WorkflowTaskDefinition startTaskDef = createWorkflowTaskDefinition(startTask);
return factory.createDefinition(id, name, version, name, null, startTaskDef);
}
/**
* * Creates a Workflow Task
* @param task
* @return
*/
protected WorkflowTask createWorkflowTask(TaskInstance task)
{
WorkflowPath path = createWorkflowPath(task.getToken());
Map<QName, Serializable> properties = getTaskProperties(task, false);
WorkflowTaskDefinition definition = createWorkflowTaskDefinition(task.getTask());
return createWorkflowTask(task, definition, path, properties);
}
/**
* Creates a Workflow Task
*
* @param task
* JBoss Task Instance
* @param taskDef
* @param path
* @param properties
* @return Workflow Task
*/
protected WorkflowTask createWorkflowTask(TaskInstance task)
private WorkflowTask createWorkflowTask(TaskInstance task, WorkflowTaskDefinition definition, WorkflowPath path, Map<QName, Serializable> properties)
{
if(task == null)
return null;
String processName = task.getTask().getProcessDefinition().getName();
if (tenantService.isEnabled())
{
tenantService.checkDomain(processName); // throws exception if
// domain mismatch
}
String id = Long.toString(task.getId());
String name = task.getName();
WorkflowPath path = createWorkflowPath(task.getToken());
WorkflowTaskState state = getWorkflowTaskState(task);
WorkflowTaskDefinition definition = createWorkflowTaskDefinition(task.getTask());
Map<QName, Serializable> properties = getTaskProperties(task, false);
return factory.createTask(id, definition, name, null, null, state, path, properties);
}
@@ -3129,6 +3329,9 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
*/
protected WorkflowTimer createWorkflowTimer(Timer timer)
{
if(timer==null)
return null;
WorkflowPath path = createWorkflowPath(timer.getToken());
WorkflowTask workflowTask = null;

View File

@@ -0,0 +1,288 @@
package org.alfresco.repo.workflow.jbpm;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.repo.workflow.BPMEngineRegistry;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.hibernate.Query;
import org.hibernate.Session;
import org.jbpm.JbpmContext;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springmodules.workflow.jbpm31.JbpmCallback;
import org.springmodules.workflow.jbpm31.JbpmTemplate;
/**
* This test shows a performance benefit from a usage of direct queries
* instead of creating required classes like WorkflowTask in a loop with collecting
* required properties from different services.
*
* @author arsenyko
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:alfresco/application-context.xml" })
public class JBPMJunit4LoadTests
{
private static String WORKFLOW_NAME = "jbpm$wf:adhoc";
private static String WORKFLOW_NODE_NAME = "workflow-test-19243cbb-c58a-485e-bcd9-2e2be030dfb9.txt";
private static int WORKFLOW_COUNT = 2000;
private static List<String> workflowIds = null;
private static NodeRef rootNode = null;
@Resource(name=ServiceRegistry.SERVICE_REGISTRY)
private ServiceRegistry serviceRegistry;
private RetryingTransactionHelper retryingTransactionHelper;
private static NodeService nodeService;
private static WorkflowService workflowService;
private FileFolderService fileFolderService;
@Resource(name="repositoryHelper")
private Repository repositoryHelper;
@Resource(name="jbpm_engine")
private JBPMEngine jbpmEngine;
private NodeRef companyHomeNodeRef;
@Before
public void setUp() throws Exception
{
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
retryingTransactionHelper = serviceRegistry.getRetryingTransactionHelper();
fileFolderService = serviceRegistry.getFileFolderService();
workflowService = serviceRegistry.getWorkflowService();
nodeService = serviceRegistry.getNodeService();
companyHomeNodeRef = repositoryHelper.getCompanyHome();
System.out.println(" -------------- ");
createWorkflowStuff();
}
public void createWorkflowStuff() throws Exception
{
System.out.println(" [createWorkflowStuff] Started at " + new Date().toString());
if (rootNode == null)
{
workflowIds =new ArrayList<String>();
RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>(){
@Override
public Void execute() throws Throwable
{
FileInfo rootInfo = fileFolderService.create(companyHomeNodeRef,
WORKFLOW_NODE_NAME,
ContentModel.TYPE_FOLDER);
rootNode = rootInfo.getNodeRef();
FileInfo contentInfo = fileFolderService.create(rootNode,
WORKFLOW_NODE_NAME,
ContentModel.TYPE_CONTENT);
NodeRef content = contentInfo.getNodeRef();
ContentService contentService = serviceRegistry.getContentService();
ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true);
writer.setMimetype("text/plain");
writer.setEncoding("UTF-8");
writer.putContent("many workflows many workflows many workflows many workflows many workflows many workflows many workflows many workflows");
System.out.println(" [createWorkflowStuff] Workflow root node '" + WORKFLOW_NODE_NAME + "' has been created");
WorkflowDefinition wfDef = jbpmEngine.getDefinitionByName(WORKFLOW_NAME);
long startTime = new Date().getTime();
for (Integer i = 0; i < WORKFLOW_COUNT; i++)
{
// We are creating workflows in usual way, but with new persistent objects.
// There is a some performance issue with sesssion.flash() in each iteration,
// but this was made to avoid a lot of changes in a logic related to org.alfresco.service.cmr.workflow.*
// classes.
Map<QName, Serializable> properties = prepareWorkflowProperties(rootNode, content, i.toString());
WorkflowPath path = workflowService.startWorkflow(wfDef.getId(), properties);
workflowIds.add(path.getInstance().getId());
// jbpmEngine.startWorkflow_ALF1787(wfDef.id, prepareWorkflowProperties(fileInfo.getNodeRef(), i.toString()));
}
long endTime = new Date().getTime();
System.out.println(" [createWorkflowStuff] Execution time (ms): " + (endTime - startTime));
return null;
}
};
retryingTransactionHelper.setMaxRetries(1);
retryingTransactionHelper.doInTransaction(callback);
System.out.println(" [createWorkflowStuff] Finished at " + new Date().toString());
}
else
{
System.out.println(" [createWorkflowStuff] Workflow node '" + WORKFLOW_NODE_NAME + "' already exists");
}
}
//@Test
// public void testQuery1() throws Exception
// {
// RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>(){
//
// @Override
// public Void execute() throws Throwable
// {
// JbpmTemplate jbpmTemplate = (JbpmTemplate) applicationContext.getBean("jbpm_template");
// List<Object[]> result = (List<Object[]>) jbpmTemplate.execute(new JbpmCallback()
// {
// public List<Object[]> doInJbpm(JbpmContext context)
// {
// Session session = context.getSession();
// Query query = session.getNamedQuery("org.alfresco.repo.workflow.findTaskInstancesByActorId");
// return query.setString("actorId", "admin").list();
// }
// });
// for(Object[] ti : result)
// {
// System.out.println(Arrays.toString(ti));
// }
// System.out.println(result.size());
// return null;
// }
// };
// retryingTransactionHelper.setMaxRetries(1);
// retryingTransactionHelper.doInTransaction(callback);
// }
@Test
public void testGetAssignedTasks_NEW() throws Exception
{
final int RUN_COUNT = 7;
RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>(){
@Override
public Void execute() throws Throwable
{
Date beginTime = new Date();
System.out.println(" [testGetAssignedTasks_NEW] Started at " + beginTime.toString());
List<WorkflowTask> tasks = workflowService.getAssignedTasks("admin", WorkflowTaskState.IN_PROGRESS);
Date endTime = new Date();
System.out.println(" [testGetAssignedTasks_NEW] Retrieved tasks: " + tasks.size() + " in " + (endTime.getTime() - beginTime.getTime()) + " ms");
System.out.println(" [testGetAssignedTasks_NEW] Finished at " + endTime.toString());
return null;
}
};
retryingTransactionHelper.setMaxRetries(1);
for(int i=0; i<RUN_COUNT; i++)
{
retryingTransactionHelper.doInTransaction(callback);
}
}
/*
@Test
public void testGetAssignedTasks_OLD() throws Exception
{
RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>(){
@Override
public Void execute() throws Throwable
{
Date beginTime = new Date();
System.out.println(" [testGetAssignedTasks_OLD] Started at " + beginTime.toString());
List<WorkflowTask> tasks = jbpmEngine.getAssignedTasks_OLD("admin", WorkflowTaskState.IN_PROGRESS);
Date endTime = new Date();
System.out.println(" [testGetAssignedTasks_OLD] Retrieved tasks: " + tasks.size() + " in " + (endTime.getTime() - beginTime.getTime()) + " ms");
System.out.println(" [testGetAssignedTasks_OLD] Finished at " + new Date().toString());
return null;
}
};
retryingTransactionHelper.setMaxRetries(1);
retryingTransactionHelper.doInTransaction(callback);
}
*/
@After
public void tearDown() throws Exception
{
System.out.println(" -------------- ");
}
private Map<QName, Serializable> prepareWorkflowProperties(NodeRef root, NodeRef content, String id)
{
NodeRef packageRef = makePackage(root, content, id);
Map<QName, Serializable> parameters = new HashMap<QName, Serializable>();
parameters.put(WorkflowModel.ASSOC_PACKAGE, packageRef);
parameters.put(WorkflowModel.ASSOC_ASSIGNEE, "admin");
parameters.put(WorkflowModel.PROP_WORKFLOW_DESCRIPTION, "Test workflow '" + id + "'");
parameters.put(WorkflowModel.PROP_WORKFLOW_DEFINITION_NAME, "test_workflow_" + id);
return parameters;
}
/**
* @param root
* @param content
* @param id
* @return
*/
private NodeRef makePackage(NodeRef root, NodeRef content, String id)
{
NodeRef container = fileFolderService.create(root, "package"+id, ContentModel.TYPE_FOLDER).getNodeRef();
NodeRef packageRef = workflowService.createPackage(container);
nodeService.addChild(packageRef, content, ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.DEFAULT_URI, id));
return packageRef;
}
@AfterClass
public static void cleanup()
{
// Clean up workflows
if(workflowIds !=null)
{
for (String wfId : workflowIds)
{
try
{
workflowService.cancelWorkflow(wfId);
}
catch(Exception e)
{
//NOOP
}
}
}
nodeService.deleteNode(rootNode);
}
}