diff --git a/config/alfresco/model/bpmModel.xml b/config/alfresco/model/bpmModel.xml index 1104cf8941..78cb147d52 100644 --- a/config/alfresco/model/bpmModel.xml +++ b/config/alfresco/model/bpmModel.xml @@ -335,6 +335,18 @@ + + + + + + + bpm:startTask + + bpm:endAutomatically + + + @@ -490,6 +502,11 @@ - + + + + + + \ No newline at end of file diff --git a/source/java/org/alfresco/repo/workflow/AlfrescoBpmEngine.java b/source/java/org/alfresco/repo/workflow/AlfrescoBpmEngine.java index 99821c2d17..9b092c1724 100644 --- a/source/java/org/alfresco/repo/workflow/AlfrescoBpmEngine.java +++ b/source/java/org/alfresco/repo/workflow/AlfrescoBpmEngine.java @@ -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.QName; /** * @author Nick Smith @@ -58,7 +59,8 @@ public abstract class AlfrescoBpmEngine extends BPMEngine throw new WorkflowException("NamespaceService not specified"); } WorkflowQNameConverter qNameConverter = new WorkflowQNameConverter(namespaceService); - this.factory = new WorkflowObjectFactory(qNameConverter, tenantService, messageService, dictionaryService, getEngineId()); + QName defaultStartTaskType = getDefaultStartTaskType(); + this.factory = new WorkflowObjectFactory(qNameConverter, tenantService, messageService, dictionaryService, getEngineId(), defaultStartTaskType); } /** @@ -114,4 +116,6 @@ public abstract class AlfrescoBpmEngine extends BPMEngine { this.authorityManager = authorityManager; } + + protected abstract QName getDefaultStartTaskType(); } diff --git a/source/java/org/alfresco/repo/workflow/WorkflowModel.java b/source/java/org/alfresco/repo/workflow/WorkflowModel.java index 4e8e99a439..1759ed7756 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowModel.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowModel.java @@ -73,6 +73,12 @@ public interface WorkflowModel // Activiti Task Constants static final QName TYPE_ACTIVTI_TASK = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "activitiOutcomeTask"); static final QName PROP_OUTCOME_PROPERTY_NAME= QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "outcomePropertyName"); + + // Activiti Start Task Constants + static final QName TYPE_ACTIVTI_START_TASK = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "activitiStartTask"); + + // Activiti Start Task Constants + static final QName ASPECT_END_AUTOMATICALLY= QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "endAutomatically"); // workflow package static final QName ASPECT_WORKFLOW_PACKAGE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowPackage"); diff --git a/source/java/org/alfresco/repo/workflow/WorkflowObjectFactory.java b/source/java/org/alfresco/repo/workflow/WorkflowObjectFactory.java index dcd89a9702..4727e03ec9 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowObjectFactory.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowObjectFactory.java @@ -62,18 +62,20 @@ public class WorkflowObjectFactory private final MessageService messageService; private final DictionaryService dictionaryService; private final String engineId; + private final QName defaultStartTaskType; public WorkflowObjectFactory(WorkflowQNameConverter qNameConverter, TenantService tenantService, MessageService messageService, DictionaryService dictionaryService, - String engineId) + String engineId, QName defaultStartTaskType) { this.tenantService = tenantService; this.messageService = messageService; this.dictionaryService = dictionaryService; this.engineId = engineId; this.qNameConverter = qNameConverter; + this.defaultStartTaskType = defaultStartTaskType; } public String buildGlobalId(String localId) @@ -390,7 +392,7 @@ public class WorkflowObjectFactory } if (typeDef == null) { - QName defaultTypeName = isStart? WorkflowModel.TYPE_START_TASK : WorkflowModel.TYPE_WORKFLOW_TASK; + QName defaultTypeName = isStart? defaultStartTaskType : WorkflowModel.TYPE_WORKFLOW_TASK; typeDef = dictionaryService.getType(defaultTypeName); if (typeDef == null) { diff --git a/source/java/org/alfresco/repo/workflow/activiti/ActivitiTypeConverter.java b/source/java/org/alfresco/repo/workflow/activiti/ActivitiTypeConverter.java index b6932811bf..9ad1ebd0f7 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/ActivitiTypeConverter.java +++ b/source/java/org/alfresco/repo/workflow/activiti/ActivitiTypeConverter.java @@ -357,14 +357,31 @@ public class ActivitiTypeConverter taskDef, taskDef.getId(), defaultTitle, defaultDescription, state, path, properties); } - public WorkflowTask getVirtualStartTask(String executionId, boolean inProgress) + public WorkflowTask getVirtualStartTask(String processInstanceId, Boolean inProgress) { - Execution execution = runtimeService.createExecutionQuery().executionId(executionId).singleResult(); - ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() - .processInstanceId(execution.getProcessInstanceId()) - .singleResult(); - - String id = ActivitiConstants.START_TASK_PREFIX + execution.getProcessInstanceId(); + ProcessInstance processInstance = activitiUtil.getProcessInstance(processInstanceId); + if(processInstance != null) + { + if(null == inProgress) + { + inProgress = isStartTaskActive(processInstanceId); + } + return getVirtualStartTask(processInstance, inProgress); + } + HistoricProcessInstance historicProcessInstance = activitiUtil.getHistoricProcessInstance(processInstanceId); + return getVirtualStartTask(historicProcessInstance); + } + + public boolean isStartTaskActive(String processInstanceId) + { + Object endDate = runtimeService.getVariable(processInstanceId, ActivitiConstants.PROP_START_TASK_END_DATE); + return endDate == null; + } + + private WorkflowTask getVirtualStartTask(ProcessInstance processInstance, boolean inProgress) + { + String processInstanceId = processInstance.getId(); + String id = ActivitiConstants.START_TASK_PREFIX + processInstanceId; WorkflowTaskState state = null; if(inProgress) @@ -376,7 +393,7 @@ public class ActivitiTypeConverter state = WorkflowTaskState.COMPLETED; } - WorkflowPath path = convert(execution); + WorkflowPath path = convert((Execution)processInstance); // Convert start-event to start-task Node ReadOnlyProcessDefinition procDef = activitiUtil.getDeployedProcessDefinition(processInstance.getProcessDefinitionId()); @@ -393,7 +410,7 @@ public class ActivitiTypeConverter // Add properties based on HistoricProcessInstance HistoricProcessInstance historicProcessInstance = historyService .createHistoricProcessInstanceQuery() - .processInstanceId(execution.getProcessInstanceId()) + .processInstanceId(processInstance.getId()) .singleResult(); Map properties = propertyConverter.getStartTaskProperties(historicProcessInstance, taskDefId, !inProgress); @@ -406,7 +423,7 @@ public class ActivitiTypeConverter taskDef, taskDef.getId(), defaultTitle, defaultDescription, state, path, properties); } - public WorkflowTask getVirtualStartTask(HistoricProcessInstance historicProcessInstance) + private WorkflowTask getVirtualStartTask(HistoricProcessInstance historicProcessInstance) { if(historicProcessInstance == null) { @@ -416,18 +433,9 @@ public class ActivitiTypeConverter String processInstanceId = historicProcessInstance.getId(); String id = ActivitiConstants.START_TASK_PREFIX + processInstanceId; - WorkflowTaskState state = null; - - boolean completed = historicProcessInstance.getEndTime() != null; - if(completed) - { - state = WorkflowTaskState.COMPLETED; - } - else - { - state = WorkflowTaskState.IN_PROGRESS; - } - + // Since the process instance is complete the Start Task must be complete! + WorkflowTaskState state = WorkflowTaskState.COMPLETED; + // We use the process-instance ID as execution-id. It's ended anyway WorkflowPath path = buildCompletedPath(processInstanceId, processInstanceId); if(path == null) @@ -442,6 +450,7 @@ public class ActivitiTypeConverter String taskDefId = activitiUtil.getStartFormKey(historicProcessInstance.getProcessDefinitionId()); WorkflowTaskDefinition taskDef = factory.createTaskDefinition(taskDefId, startNode, taskDefId, true); + boolean completed = historicProcessInstance.getEndTime() != null; Map properties = propertyConverter.getStartTaskProperties(historicProcessInstance, taskDefId, completed); // TODO: Figure out what name/description should be used for the start-task, start event's name? diff --git a/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java b/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java index c2b364af0c..39cb630add 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java +++ b/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java @@ -33,6 +33,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import org.activiti.engine.ActivitiException; import org.activiti.engine.FormService; @@ -81,6 +82,7 @@ import org.alfresco.repo.workflow.WorkflowModel; import org.alfresco.repo.workflow.WorkflowNodeConverter; import org.alfresco.repo.workflow.WorkflowObjectFactory; import org.alfresco.repo.workflow.activiti.properties.ActivitiPropertyConverter; +import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -689,46 +691,42 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine /** * {@inheritDoc} */ - public List getTasksForWorkflowPath(String pathId) { - try - { - // Extract the Activiti ID from the path - String executionId = getExecutionIdFromPath(pathId); - if(executionId == null) - { - throw new WorkflowException(messageService.getMessage(ERR_GET_WORKFLOW_TOKEN_INVALID, pathId)); - } - - // Check if the execution exists - Execution execution = runtimeService.createExecutionQuery().executionId(executionId).singleResult(); - if(execution == null) - { - throw new WorkflowException(messageService.getMessage(ERR_GET_WORKFLOW_TOKEN_NULL, pathId)); - } - - List resultList = new ArrayList(); - - // Check if workflow's start task has been completed. If not, return the virtual task - // Otherwise, just return the runtime activiti tasks - Date startTaskEndDate = (Date) runtimeService.getVariable(execution.getProcessInstanceId(), - ActivitiConstants.PROP_START_TASK_END_DATE); - boolean startTaskEnded = (startTaskEndDate != null); - - if(startTaskEnded) - { - List tasks = taskService.createTaskQuery().executionId(executionId).list(); - for(Task task : tasks) - { - resultList.add(typeConverter.convert(task)); - } - } - else - { - resultList.add(typeConverter.getVirtualStartTask(executionId, true)); - } - return resultList; + try + { + // Extract the Activiti ID from the path + String executionId = getExecutionIdFromPath(pathId); + if (executionId == null) + { + throw new WorkflowException(messageService.getMessage(ERR_GET_WORKFLOW_TOKEN_INVALID, pathId)); + } + + // Check if the execution exists + Execution execution = runtimeService.createExecutionQuery().executionId(executionId).singleResult(); + if (execution == null) + { + throw new WorkflowException(messageService.getMessage(ERR_GET_WORKFLOW_TOKEN_NULL, pathId)); + } + + // Check if workflow's start task has been completed. If not, return + // the virtual task + // Otherwise, just return the runtime Activiti tasks + String processInstanceId = execution.getProcessInstanceId(); + ArrayList resultList = new ArrayList(); + if (typeConverter.isStartTaskActive(processInstanceId)) + { + resultList.add(typeConverter.getVirtualStartTask(processInstanceId, true)); + } + else + { + List tasks = taskService.createTaskQuery().executionId(executionId).list(); + for (Task task : tasks) + { + resultList.add(typeConverter.convert(task)); + } + } + return resultList; } catch (ActivitiException ae) { @@ -736,7 +734,7 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine throw new WorkflowException(msg, ae); } } - + protected String getExecutionIdFromPath(String workflowPath) { if(workflowPath != null) @@ -1017,7 +1015,9 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine } else { - return typeConverter.convert((Execution)instance); + WorkflowPath path = typeConverter.convert((Execution)instance); + endStartTaskAutomatically(path, instance); + return path; } } catch (ActivitiException ae) @@ -1026,6 +1026,23 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine throw new WorkflowException(msg, ae); } } + + /** + * @param path + * @param instance + */ + private void endStartTaskAutomatically(WorkflowPath path, ProcessInstance instance) + { + // Check if StartTask Needs to be ended automatically + WorkflowDefinition definition = path.getInstance().getDefinition(); + TypeDefinition metadata = definition.getStartTaskDefinition().getMetadata(); + Set aspects = metadata.getDefaultAspectNames(); + if(aspects.contains(WorkflowModel.ASPECT_END_AUTOMATICALLY)) + { + String taskId = ActivitiConstants.START_TASK_PREFIX + instance.getId(); + endStartTask(taskId); + } + } /** * {@inheritDoc} @@ -1296,7 +1313,7 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine // Check if the task is a virtual start task if(localTaskId.startsWith(ActivitiConstants.START_TASK_PREFIX)) { - return endStartTask(taskId, localTaskId, transition); + return endStartTask(localTaskId); } return endNormalTask(taskId, localTaskId, transition); @@ -1356,11 +1373,15 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine propertyConverter.updateTask(task, updates, null, null); } - private WorkflowTask endStartTask(String taskId, String localTaskId, String transition) + private WorkflowTask endStartTask(String localTaskId) { // We don't end a task, we set a variable on the process-instance // to indicate that it's started String processInstanceId = localTaskId.replace(ActivitiConstants.START_TASK_PREFIX, ""); + if(false == typeConverter.isStartTaskActive(processInstanceId)) + { + return typeConverter.getVirtualStartTask(processInstanceId, false); + } // Set start task end date on the process runtimeService.setVariable(processInstanceId, ActivitiConstants.PROP_START_TASK_END_DATE, new Date()); @@ -1379,22 +1400,10 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine runtimeService.signal(processInstanceId); // It's possible the process has ended after signalling the receive task - processInstance = activitiUtil.getProcessInstance(processInstanceId); - if (processInstance != null) - { - return typeConverter.getVirtualStartTask(processInstanceId, false); - } - else - { - return typeConverter.getVirtualStartTask(activitiUtil.getHistoricProcessInstance(processInstanceId)); - } } - else - { - // Return virtual start task for the execution, it's safe to use the processInstanceId - return typeConverter.getVirtualStartTask(processInstanceId, false); - } - + // Return virtual start task for the execution, it's safe to use the + // processInstanceId + return typeConverter.getVirtualStartTask(processInstanceId, false); } /** @@ -1515,7 +1524,7 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine if(localId.startsWith(ActivitiConstants.START_TASK_PREFIX)) { String processInstanceId = localId.replace(ActivitiConstants.START_TASK_PREFIX ,""); - return getVirtualStartTaskForProcessInstance(processInstanceId); + return typeConverter.getVirtualStartTask(processInstanceId, null); } else { @@ -1884,8 +1893,7 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine // Only return start-task when a process or task id is set if(processInstanceId != null) { - // Extract processInstanceId - WorkflowTask workflowTask = getVirtualStartTaskForProcessInstance(processInstanceId); + WorkflowTask workflowTask = typeConverter.getVirtualStartTask(processInstanceId, null); if(workflowTask != null) { boolean startTaskMatches = isStartTaskMatching(workflowTask, query); @@ -2033,32 +2041,7 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine public WorkflowTask getStartTask(String workflowInstanceId) { String instanceId = createLocalId(workflowInstanceId); - return getVirtualStartTaskForProcessInstance(instanceId); - } - - public WorkflowTask getVirtualStartTaskForProcessInstance(String processInstanceId) - { - - ProcessInstance runningInstance = runtimeService.createProcessInstanceQuery() - .processInstanceId(processInstanceId) - .singleResult(); - - if(runningInstance != null) - { - // Check the process instance variable to see if start-task has been completed - Date startTaskEndDate = (Date) runtimeService.getVariable(runningInstance.getProcessInstanceId(), - ActivitiConstants.PROP_START_TASK_END_DATE); - boolean startTaskEnded = (startTaskEndDate != null); - return typeConverter.getVirtualStartTask(runningInstance.getId(), !startTaskEnded); - } - else - { - HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery() - .processInstanceId(processInstanceId) - .singleResult(); - - return typeConverter.getVirtualStartTask(hpi); - } + return typeConverter.getVirtualStartTask(instanceId, null); } /** diff --git a/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowManagerFactory.java b/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowManagerFactory.java index 2d27dd937b..4b3199338d 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowManagerFactory.java +++ b/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowManagerFactory.java @@ -26,6 +26,7 @@ import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.workflow.BPMEngineRegistry; import org.alfresco.repo.workflow.DefaultWorkflowPropertyHandler; import org.alfresco.repo.workflow.WorkflowAuthorityManager; +import org.alfresco.repo.workflow.WorkflowModel; import org.alfresco.repo.workflow.WorkflowObjectFactory; import org.alfresco.repo.workflow.WorkflowPropertyHandlerRegistry; import org.alfresco.repo.workflow.WorkflowQNameConverter; @@ -37,6 +38,7 @@ import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.workflow.WorkflowException; import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; import org.springframework.beans.factory.FactoryBean; /** @@ -90,7 +92,8 @@ public class ActivitiWorkflowManagerFactory implements FactoryBean tasks = workflowService.getTasksForWorkflowPath(path.getId()); + assertEquals(1, tasks.size()); + String taskName = tasks.get(0).getName(); + assertEquals("bpm_foo_task", taskName); + } + @Override protected void checkTaskQueryStartTaskCompleted(String workflowInstanceId, WorkflowTask startTask) { diff --git a/source/java/org/alfresco/repo/workflow/activiti/properties/ActivitiPropertyConverter.java b/source/java/org/alfresco/repo/workflow/activiti/properties/ActivitiPropertyConverter.java index 906979453e..9224dc848a 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/properties/ActivitiPropertyConverter.java +++ b/source/java/org/alfresco/repo/workflow/activiti/properties/ActivitiPropertyConverter.java @@ -852,12 +852,10 @@ public class ActivitiPropertyConverter */ private List getMissingMandatoryTaskProperties(DelegateTask task) { - TypeDefinition typeDefinition = typeManager.getFullTaskDefinition(task); // retrieve properties of task Map existingValues = getTaskProperties(task, typeDefinition, false); - // retrieve definition of task Map propertyDefs = typeDefinition.getProperties(); Map assocDefs = typeDefinition.getAssociations(); diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java index c4f26c0951..80d4f0804d 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java @@ -3417,7 +3417,7 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine { if (node instanceof HibernateProxy) { - Node realNode = (Node)((HibernateProxy)node).getHibernateLazyInitializer().getImplementation(); + Node realNode = (Node)((HibernateProxy)node).getHibernateLazyInitializer().getImplementation(); return realNode; } else @@ -3426,4 +3426,10 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine } } + @Override + protected QName getDefaultStartTaskType() + { + return WorkflowModel.TYPE_START_TASK; + } + }