Workflow Service - addition of task query

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5720 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
David Caruana
2007-05-18 12:35:22 +00:00
parent 6a5a8cbe75
commit 801ec46791
6 changed files with 552 additions and 0 deletions

View File

@@ -30,6 +30,7 @@ import java.util.Map;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.workflow.WorkflowTask; import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskQuery;
import org.alfresco.service.cmr.workflow.WorkflowTaskState; import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
@@ -67,6 +68,14 @@ public interface TaskComponent
*/ */
public List<WorkflowTask> getPooledTasks(List<String> authorities); public List<WorkflowTask> getPooledTasks(List<String> authorities);
/**
* Query for tasks
*
* @param query the filter by which tasks are queried
* @return the list of tasks matching the specified query
*/
public List<WorkflowTask> queryTasks(WorkflowTaskQuery query);
/** /**
* Update the Properties and Associations of a Task * Update the Properties and Associations of a Task
* *

View File

@@ -42,6 +42,7 @@ import org.alfresco.service.cmr.workflow.WorkflowInstance;
import org.alfresco.service.cmr.workflow.WorkflowPath; import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowService; import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.cmr.workflow.WorkflowTask; import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskQuery;
import org.alfresco.service.cmr.workflow.WorkflowTaskState; import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@@ -327,6 +328,46 @@ public class WorkflowServiceImpl implements WorkflowService
return Collections.unmodifiableList(tasks); return Collections.unmodifiableList(tasks);
} }
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#queryTasks(org.alfresco.service.cmr.workflow.WorkflowTaskFilter)
*/
public List<WorkflowTask> queryTasks(WorkflowTaskQuery query)
{
// extract task component to perform query
String engineId = null;
String processId = query.getProcessId();
if (processId != null)
{
engineId = BPMEngineRegistry.getEngineId(processId);
}
String taskId = query.getTaskId();
if (taskId != null)
{
String taskEngineId = BPMEngineRegistry.getEngineId(taskId);
if (engineId != null && !engineId.equals(taskEngineId))
{
throw new WorkflowException("Cannot query for tasks across multiple task components: " + engineId + ", " + taskEngineId);
}
engineId = taskEngineId;
}
// perform query
List<WorkflowTask> tasks = new ArrayList<WorkflowTask>(10);
String[] ids = registry.getTaskComponents();
for (String id: ids)
{
TaskComponent component = registry.getTaskComponent(id);
// NOTE: don't bother asking task component if specific task or process id
// are in the filter and do not correspond to the component
if (engineId != null && !engineId.equals(id))
{
continue;
}
tasks.addAll(component.queryTasks(query));
}
return Collections.unmodifiableList(tasks);
}
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#updateTask(java.lang.String, java.util.Map, java.util.Map, java.util.Map) * @see org.alfresco.service.cmr.workflow.WorkflowService#updateTask(java.lang.String, java.util.Map, java.util.Map, java.util.Map)
*/ */

View File

@@ -40,6 +40,9 @@ import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowInstance; import org.alfresco.service.cmr.workflow.WorkflowInstance;
import org.alfresco.service.cmr.workflow.WorkflowPath; import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowService; import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskQuery;
import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseSpringTest; import org.alfresco.util.BaseSpringTest;
@@ -94,6 +97,29 @@ public class WorkflowServiceImplTest extends BaseSpringTest
assertTrue(nodeService.hasAspect(nodeRef, WorkflowModel.ASPECT_WORKFLOW_PACKAGE)); assertTrue(nodeService.hasAspect(nodeRef, WorkflowModel.ASPECT_WORKFLOW_PACKAGE));
} }
public void testQueryTasks()
{
WorkflowTaskQuery filter = new WorkflowTaskQuery();
filter.setTaskName(QName.createQName("{http://www.alfresco.org/model/wcmworkflow/1.0}submitpendingTask"));
filter.setTaskState(WorkflowTaskState.COMPLETED);
Map<QName, Object> taskProps = new HashMap<QName, Object>();
taskProps.put(QName.createQName("{http://www.alfresco.org/model/bpm/1.0}workflowDescription"), "Test5");
filter.setTaskCustomProps(taskProps);
filter.setProcessId("jbpm$48");
filter.setProcessName(QName.createQName("{http://www.alfresco.org/model/wcmworkflow/1.0}submit"));
Map<QName, Object> procProps = new HashMap<QName, Object>();
procProps.put(QName.createQName("{http://www.alfresco.org/model/bpm/1.0}workflowDescription"), "Test5");
procProps.put(QName.createQName("companyhome"), new NodeRef("workspace://SpacesStore/3df8a9d0-ff04-11db-98da-a3c3f3149ea5"));
filter.setProcessCustomProps(procProps);
filter.setOrderBy(new WorkflowTaskQuery.OrderBy[] { WorkflowTaskQuery.OrderBy.TaskName_Asc, WorkflowTaskQuery.OrderBy.TaskState_Asc });
List<WorkflowTask> tasks = workflowService.queryTasks(filter);
System.out.println("Found " + tasks.size() + " tasks.");
for (WorkflowTask task : tasks)
{
System.out.println(task.toString());
}
}
public void testAssociateWorkflowPackage() public void testAssociateWorkflowPackage()
{ {
// create workflow package // create workflow package

View File

@@ -68,18 +68,26 @@ import org.alfresco.service.cmr.workflow.WorkflowNode;
import org.alfresco.service.cmr.workflow.WorkflowPath; import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowTask; import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition; import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
import org.alfresco.service.cmr.workflow.WorkflowTaskQuery;
import org.alfresco.service.cmr.workflow.WorkflowTaskState; import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.cmr.workflow.WorkflowTransition; import org.alfresco.service.cmr.workflow.WorkflowTransition;
import org.alfresco.service.namespace.NamespaceException; import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.hibernate.Criteria;
import org.hibernate.Query; import org.hibernate.Query;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.HibernateProxy;
import org.jbpm.JbpmContext; import org.jbpm.JbpmContext;
import org.jbpm.JbpmException; import org.jbpm.JbpmException;
import org.jbpm.context.exe.ContextInstance; import org.jbpm.context.exe.ContextInstance;
import org.jbpm.context.exe.TokenVariableMap; import org.jbpm.context.exe.TokenVariableMap;
import org.jbpm.context.exe.VariableInstance;
import org.jbpm.db.GraphSession; import org.jbpm.db.GraphSession;
import org.jbpm.db.TaskMgmtSession; import org.jbpm.db.TaskMgmtSession;
import org.jbpm.file.def.FileDefinition; import org.jbpm.file.def.FileDefinition;
@@ -869,6 +877,229 @@ public class JBPMEngine extends BPMEngine
} }
} }
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.TaskComponent#queryTasks(org.alfresco.service.cmr.workflow.WorkflowTaskFilter)
*/
@SuppressWarnings("unchecked")
public List<WorkflowTask> queryTasks(final WorkflowTaskQuery query)
{
try
{
return (List<WorkflowTask>) jbpmTemplate.execute(new JbpmCallback()
{
public List<WorkflowTask> doInJbpm(JbpmContext context)
{
Session session = context.getSession();
Criteria criteria = createTaskQueryCriteria(session, query);
List<TaskInstance> tasks = criteria.list();
// convert tasks to appropriate service response format
List<WorkflowTask> workflowTasks = new ArrayList<WorkflowTask>(tasks.size());
for (TaskInstance task : tasks)
{
WorkflowTask workflowTask = createWorkflowTask(task);
workflowTasks.add(workflowTask);
}
return workflowTasks;
}
});
}
catch(JbpmException e)
{
throw new WorkflowException("Failed to query tasks", e);
}
}
/**
* Construct a JBPM Hibernate query based on the Task Query provided
*
* @param session
* @param query
* @return jbpm hiberate query criteria
*/
private Criteria createTaskQueryCriteria(Session session, WorkflowTaskQuery query)
{
Criteria process = null;
Criteria task = session.createCriteria(TaskInstance.class);
// task id
if (query.getTaskId() != null)
{
task.add(Restrictions.eq("id", getJbpmId(query.getTaskId())));
}
// task state
if (query.getTaskState() != null)
{
WorkflowTaskState state = query.getTaskState();
if (state == WorkflowTaskState.IN_PROGRESS)
{
task.add(Restrictions.eq("isOpen", true));
task.add(Restrictions.isNull("end"));
}
else if (state == WorkflowTaskState.COMPLETED)
{
task.add(Restrictions.eq("isOpen", false));
task.add(Restrictions.isNotNull("end"));
}
}
// task name
if (query.getTaskName() != null)
{
task.add(Restrictions.eq("name", query.getTaskName().toPrefixString(namespaceService)));
}
// task actor
if (query.getActorId() != null)
{
task.add(Restrictions.eq("actorId", query.getActorId()));
}
// task custom properties
if (query.getTaskCustomProps() != null)
{
Map<QName, Object> props = query.getTaskCustomProps();
if (props.size() > 0)
{
Criteria variables = task.createCriteria("variableInstances");
Disjunction values = Restrictions.disjunction();
for (Map.Entry<QName, Object> prop : props.entrySet())
{
Conjunction value = Restrictions.conjunction();
value.add(Restrictions.eq("name", mapQNameToName(prop.getKey())));
value.add(Restrictions.eq("value", prop.getValue().toString()));
values.add(value);
}
variables.add(values);
}
}
// process active?
if (query.isActive() != null)
{
process = (process == null) ? task.createCriteria("processInstance") : process;
if (query.isActive())
{
process.add(Restrictions.isNull("end"));
}
else
{
process.add(Restrictions.isNotNull("end"));
}
}
// process id
if (query.getProcessId() != null)
{
process = (process == null) ? task.createCriteria("processInstance") : process;
process.add(Restrictions.eq("id", getJbpmId(query.getProcessId())));
}
// process name
if (query.getProcessName() != null)
{
process = (process == null) ? task.createCriteria("processInstance") : process;
Criteria processDef = process.createCriteria("processDefinition");
processDef.add(Restrictions.eq("name", query.getProcessName().toPrefixString(namespaceService)));
}
// process custom properties
if (query.getProcessCustomProps() != null)
{
// TODO: Due to Hibernate bug http://opensource.atlassian.com/projects/hibernate/browse/HHH-957
// it's not possible to perform a sub-select with the criteria api. For now issue a
// secondary query and create an IN clause.
Map<QName, Object> props = query.getProcessCustomProps();
if (props.size() > 0)
{
// create criteria for process variables
Criteria variables = session.createCriteria(VariableInstance.class);
variables.setProjection(Property.forName("processInstance"));
Disjunction values = Restrictions.disjunction();
for (Map.Entry<QName, Object> prop : props.entrySet())
{
Conjunction value = Restrictions.conjunction();
value.add(Restrictions.eq("name", mapQNameToName(prop.getKey())));
value.add(Restrictions.eq("value", prop.getValue().toString()));
values.add(value);
}
variables.add(values);
// retrieve list of processes matching specified variables
List<ProcessInstance> processList = variables.list();
Object[] processIds = null;
if (processList.size() == 0)
{
processIds = new Object[] { new Long(-1) };
}
else
{
processIds = new Object[processList.size()];
for (int i = 0; i < processList.size(); i++)
{
processIds[i] = processList.get(i).getId();
}
}
// constrain tasks by process list
process = (process == null) ? task.createCriteria("processInstance") : process;
process.add(Restrictions.in("id", processIds));
}
}
// order by
if (query.getOrderBy() != null)
{
WorkflowTaskQuery.OrderBy[] orderBy = query.getOrderBy();
for (WorkflowTaskQuery.OrderBy orderByPart : orderBy)
{
if (orderByPart == WorkflowTaskQuery.OrderBy.TaskActor_Asc)
{
task.addOrder(Order.asc("actorId"));
}
else if (orderByPart == WorkflowTaskQuery.OrderBy.TaskActor_Desc)
{
task.addOrder(Order.desc("actorId"));
}
else if (orderByPart == WorkflowTaskQuery.OrderBy.TaskCreated_Asc)
{
task.addOrder(Order.asc("create"));
}
else if (orderByPart == WorkflowTaskQuery.OrderBy.TaskCreated_Desc)
{
task.addOrder(Order.desc("create"));
}
else if (orderByPart == WorkflowTaskQuery.OrderBy.TaskId_Asc)
{
task.addOrder(Order.asc("id"));
}
else if (orderByPart == WorkflowTaskQuery.OrderBy.TaskId_Desc)
{
task.addOrder(Order.desc("id"));
}
else if (orderByPart == WorkflowTaskQuery.OrderBy.TaskName_Asc)
{
task.addOrder(Order.asc("name"));
}
else if (orderByPart == WorkflowTaskQuery.OrderBy.TaskName_Desc)
{
task.addOrder(Order.desc("name"));
}
else if (orderByPart == WorkflowTaskQuery.OrderBy.TaskState_Asc)
{
task.addOrder(Order.asc("end"));
}
else if (orderByPart == WorkflowTaskQuery.OrderBy.TaskState_Desc)
{
task.addOrder(Order.desc("end"));
}
}
}
return task;
}
/** /**
* Gets a jBPM Task Instance * Gets a jBPM Task Instance
* @param taskSession jBPM task session * @param taskSession jBPM task session

View File

@@ -257,6 +257,15 @@ public interface WorkflowService
@Auditable(parameters = {"authority"}) @Auditable(parameters = {"authority"})
public List<WorkflowTask> getPooledTasks(String authority); public List<WorkflowTask> getPooledTasks(String authority);
/**
* Query for tasks
*
* @param query the filter by which tasks are queried
* @return the list of tasks matching the specified query
*/
@Auditable(parameters = {"filter"})
public List<WorkflowTask> queryTasks(WorkflowTaskQuery query);
/** /**
* Update the Properties and Associations of a Task * Update the Properties and Associations of a Task
* *

View File

@@ -0,0 +1,236 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.workflow;
import java.util.Map;
import org.alfresco.service.namespace.QName;
/**
* Workflow Task Query
*
* Provides support for setting predicates and order by.
*
* @author davidc
*/
public class WorkflowTaskQuery
{
// task predicates
private String taskId;
private WorkflowTaskState taskState = WorkflowTaskState.IN_PROGRESS;
private QName taskName;
private String actorId;
private Map<QName, Object> taskCustomProps;
// process predicates
private String processId;
private QName processName;
private Boolean active = Boolean.TRUE;
private Map<QName, Object> processCustomProps;
// order by
private OrderBy[] orderBy;
/**
* Order By Columns
*/
public enum OrderBy
{
TaskId_Asc,
TaskId_Desc,
TaskCreated_Asc,
TaskCreated_Desc,
TaskName_Asc,
TaskName_Desc,
TaskActor_Asc,
TaskActor_Desc,
TaskState_Asc,
TaskState_Desc
};
/**
* @param orderBy
*/
public void setOrderBy(OrderBy[] orderBy)
{
this.orderBy = orderBy;
}
/**
* @return
*/
public OrderBy[] getOrderBy()
{
return orderBy;
}
/**
* @return
*/
public String getTaskId()
{
return taskId;
}
/**
* @param taskId
*/
public void setTaskId(String taskId)
{
this.taskId = taskId;
}
/**
* @return
*/
public Map<QName, Object> getTaskCustomProps()
{
return taskCustomProps;
}
/**
* @param taskCustomProps
*/
public void setTaskCustomProps(Map<QName, Object> taskCustomProps)
{
this.taskCustomProps = taskCustomProps;
}
/**
* @return
*/
public WorkflowTaskState getTaskState()
{
return taskState;
}
/**
* @param taskState
*/
public void setTaskState(WorkflowTaskState taskState)
{
this.taskState = taskState;
}
/**
* @return
*/
public QName getTaskName()
{
return taskName;
}
/**
* @param taskName
*/
public void setTaskName(QName taskName)
{
this.taskName = taskName;
}
/**
* @return
*/
public String getActorId()
{
return actorId;
}
/**
* @param actorId
*/
public void setActorId(String actorId)
{
this.actorId = actorId;
}
/**
* @return
*/
public String getProcessId()
{
return processId;
}
/**
* @param processId
*/
public void setProcessId(String processId)
{
this.processId = processId;
}
/**
* @return
*/
public QName getProcessName()
{
return processName;
}
/**
* @param processName
*/
public void setProcessName(QName processName)
{
this.processName = processName;
}
/**
* @return
*/
public Boolean isActive()
{
return active;
}
/**
* @param active
*/
public void setActive(Boolean active)
{
this.active = active;
}
/**
* @return
*/
public Map<QName, Object> getProcessCustomProps()
{
return processCustomProps;
}
/**
* @param processCustomProps
*/
public void setProcessCustomProps(Map<QName, Object> processCustomProps)
{
this.processCustomProps = processCustomProps;
}
}