Java tutorial
/* * #%L * Alfresco Repository * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.repo.workflow; import java.io.InputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; 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.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.workflow.WorkflowAdminService; import org.alfresco.service.cmr.workflow.WorkflowDefinition; import org.alfresco.service.cmr.workflow.WorkflowDeployment; import org.alfresco.service.cmr.workflow.WorkflowException; import org.alfresco.service.cmr.workflow.WorkflowInstance; import org.alfresco.service.cmr.workflow.WorkflowInstanceQuery; 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.WorkflowTaskDefinition; 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.namespace.QName; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.collections.CollectionUtils; import org.alfresco.util.collections.Function; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Default Alfresco Workflow Service whose implementation is backed by * registered BPM Engine plug-in components. * * @author davidc */ public class WorkflowServiceImpl implements WorkflowService { // Logging support private static Log logger = LogFactory.getLog("org.alfresco.repo.workflow"); // Dependent services private TransactionService transactionService; private AuthorityService authorityService; private BPMEngineRegistry registry; private WorkflowPackageComponent workflowPackageComponent; private NodeService nodeService; private ContentService contentService; private DictionaryService dictionaryService; private NodeService protectedNodeService; private WorkflowNotificationUtils workflowNotificationUtils; private WorkflowAdminService workflowAdminService; private int maxAuthoritiesForPooledTasks = 100; private int maxPooledTasks = -1; private boolean deployWorkflowsInTenant = false; /** * @param transactionService service that tells if the server is read-only or not */ public void setTransactionService(TransactionService transactionService) { this.transactionService = transactionService; } /** * Sets the Authority Service * * @param authorityService AuthorityService */ public void setAuthorityService(AuthorityService authorityService) { this.authorityService = authorityService; } /** * Sets the BPM Engine Registry * * @param registry bpm engine registry */ public void setBPMEngineRegistry(BPMEngineRegistry registry) { this.registry = registry; } /** * @param workflowAdminService the workflowAdminService to set */ public void setWorkflowAdminService(WorkflowAdminService workflowAdminService) { this.workflowAdminService = workflowAdminService; } /** * Sets the Workflow Package Component * * @param workflowPackageComponent workflow package component */ public void setWorkflowPackageComponent(WorkflowPackageComponent workflowPackageComponent) { this.workflowPackageComponent = workflowPackageComponent; } /** * Sets the Node Service * * @param nodeService NodeService */ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } /** * Sets the Content Service * * @param contentService ContentService */ public void setContentService(ContentService contentService) { this.contentService = contentService; } /** * Set the dictionary service * * @param dictionaryService DictionaryService */ public void setDictionaryService(DictionaryService dictionaryService) { this.dictionaryService = dictionaryService; } /** * Set the node service which applies permissions * * @param protectedNodeService NodeService */ public void setProtectedNodeService(NodeService protectedNodeService) { this.protectedNodeService = protectedNodeService; } /** * Set the workflow notification utils * * @param service workflow notification utils */ public void setWorkflowNotification(WorkflowNotificationUtils service) { this.workflowNotificationUtils = service; } /** * Sets the maximum number of groups to check for pooled tasks. For performance reasons, this is limited to 100 by * default. * * @param maxAuthoritiesForPooledTasks * the limit to set. If this is less than or equal to zero then there is no limit. */ public void setMaxAuthoritiesForPooledTasks(int maxAuthoritiesForPooledTasks) { this.maxAuthoritiesForPooledTasks = maxAuthoritiesForPooledTasks; } /** * Sets the maximum number of pooled tasks to return in a query. It may be necessary to limit this depending on UI * limitations. * * @param maxPooledTasks * the limit to set. If this is less than or equal to zero then there is no limit. */ public void setMaxPooledTasks(int maxPooledTasks) { this.maxPooledTasks = maxPooledTasks; } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#deployDefinition(java * .lang.String, java.io.InputStream, java.lang.String) */ public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype) { return deployDefinition(engineId, workflowDefinition, mimetype, null); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#deployDefinition(java * .lang.String, java.io.InputStream, java.lang.String, java.lang.String) */ public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype, String name) { WorkflowComponent component = getWorkflowComponent(engineId); WorkflowDeployment deployment = component.deployDefinition(workflowDefinition, mimetype, name); if (logger.isDebugEnabled() && deployment.getProblems().length > 0) { for (String problem : deployment.getProblems()) { logger.debug( "Workflow definition '" + deployment.getDefinition().getTitle() + "' problem: " + problem); } } return deployment; } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#isDefinitionDeployed * (org.alfresco.service.cmr.repository.NodeRef) */ public boolean isDefinitionDeployed(NodeRef workflowDefinition) { if (!nodeService.getType(workflowDefinition).equals(WorkflowModel.TYPE_WORKFLOW_DEF)) { throw new WorkflowException("Node " + workflowDefinition + " is not of type 'bpm:workflowDefinition'"); } String engineId = (String) nodeService.getProperty(workflowDefinition, WorkflowModel.PROP_WORKFLOW_DEF_ENGINE_ID); ContentReader contentReader = contentService.getReader(workflowDefinition, ContentModel.PROP_CONTENT); return isDefinitionDeployed(engineId, contentReader.getContentInputStream(), contentReader.getMimetype()); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#isDefinitionDeployed * (java.lang.String, java.io.InputStream, java.lang.String) */ public boolean isDefinitionDeployed(String engineId, InputStream workflowDefinition, String mimetype) { WorkflowComponent component = getWorkflowComponent(engineId); return component.isDefinitionDeployed(workflowDefinition, mimetype); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#deployDefinition(org * .alfresco.service.cmr.repository.NodeRef) */ public WorkflowDeployment deployDefinition(NodeRef definitionContent) { if (!nodeService.getType(definitionContent).equals(WorkflowModel.TYPE_WORKFLOW_DEF)) { throw new WorkflowException("Node " + definitionContent + " is not of type 'bpm:workflowDefinition'"); } String engineId = (String) nodeService.getProperty(definitionContent, WorkflowModel.PROP_WORKFLOW_DEF_ENGINE_ID); ContentReader contentReader = contentService.getReader(definitionContent, ContentModel.PROP_CONTENT); return deployDefinition(engineId, contentReader.getContentInputStream(), contentReader.getMimetype()); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#undeployDefinition( * java.lang.String) */ public void undeployDefinition(String workflowDefinitionId) { String engineId = BPMEngineRegistry.getEngineId(workflowDefinitionId); WorkflowComponent component = getWorkflowComponent(engineId); component.undeployDefinition(workflowDefinitionId); } /* * (non-Javadoc) * @see org.alfresco.service.cmr.workflow.WorkflowService#getDefinitions() */ public List<WorkflowDefinition> getDefinitions() { List<WorkflowDefinition> definitions = new ArrayList<WorkflowDefinition>(10); String[] ids = registry.getWorkflowComponents(); for (String id : ids) { if (workflowAdminService.isEngineVisible(id)) { WorkflowComponent component = registry.getWorkflowComponent(id); definitions.addAll(component.getDefinitions()); } } return Collections.unmodifiableList(definitions); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getAllDefinitions() */ public List<WorkflowDefinition> getAllDefinitions() { List<WorkflowDefinition> definitions = new ArrayList<WorkflowDefinition>(10); String[] ids = registry.getWorkflowComponents(); for (String id : ids) { if (workflowAdminService.isEngineVisible(id)) { WorkflowComponent component = registry.getWorkflowComponent(id); definitions.addAll(component.getAllDefinitions()); } } return Collections.unmodifiableList(definitions); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getDefinitionById(java * .lang.String) */ public WorkflowDefinition getDefinitionById(String workflowDefinitionId) { String engineId = BPMEngineRegistry.getEngineId(workflowDefinitionId); WorkflowComponent component = getWorkflowComponent(engineId); return component.getDefinitionById(workflowDefinitionId); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getDefinitionByName * (java.lang.String) */ public WorkflowDefinition getDefinitionByName(String workflowName) { String engineId = BPMEngineRegistry.getEngineId(workflowName); WorkflowComponent component = getWorkflowComponent(engineId); return component.getDefinitionByName(workflowName); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getAllDefinitionsByName * (java.lang.String) */ public List<WorkflowDefinition> getAllDefinitionsByName(String workflowName) { String engineId = BPMEngineRegistry.getEngineId(workflowName); WorkflowComponent component = getWorkflowComponent(engineId); return component.getAllDefinitionsByName(workflowName); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getDefinitionImage( * java.lang.String) */ public byte[] getDefinitionImage(String workflowDefinitionId) { String engineId = BPMEngineRegistry.getEngineId(workflowDefinitionId); WorkflowComponent component = getWorkflowComponent(engineId); byte[] definitionImage = component.getDefinitionImage(workflowDefinitionId); if (definitionImage == null) { definitionImage = new byte[0]; } return definitionImage; } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getAllTaskDefinitions * (java.lang.String) */ public List<WorkflowTaskDefinition> getTaskDefinitions(final String workflowDefinitionId) { String engineId = BPMEngineRegistry.getEngineId(workflowDefinitionId); WorkflowComponent component = getWorkflowComponent(engineId); return component.getTaskDefinitions(workflowDefinitionId); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#startWorkflow(java. * lang.String, java.util.Map) */ public WorkflowPath startWorkflow(String workflowDefinitionId, Map<QName, Serializable> parameters) { String engineId = BPMEngineRegistry.getEngineId(workflowDefinitionId); WorkflowComponent component = getWorkflowComponent(engineId); WorkflowPath path = component.startWorkflow(workflowDefinitionId, parameters); if (path != null && parameters != null && parameters.containsKey(WorkflowModel.ASSOC_PACKAGE)) { WorkflowInstance instance = path.getInstance(); workflowPackageComponent.setWorkflowForPackage(instance); } return path; } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#startWorkflowFromTemplate * (org.alfresco.service.cmr.repository.NodeRef) */ public WorkflowPath startWorkflowFromTemplate(NodeRef templateDefinition) { // TODO throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public List<WorkflowInstance> getActiveWorkflows(String workflowDefinitionId) { return getWorkflows(new WorkflowInstanceQuery(workflowDefinitionId, true)); } /** * {@inheritDoc} */ public List<WorkflowInstance> getCompletedWorkflows(String workflowDefinitionId) { return getWorkflows(new WorkflowInstanceQuery(workflowDefinitionId, false)); } /** * {@inheritDoc} */ public List<WorkflowInstance> getWorkflows(String workflowDefinitionId) { return getWorkflows(new WorkflowInstanceQuery(workflowDefinitionId)); } @Override public List<WorkflowInstance> getWorkflows(WorkflowInstanceQuery workflowInstanceQuery) { return getWorkflows(workflowInstanceQuery, 0, 0); } @Override public List<WorkflowInstance> getWorkflows(final WorkflowInstanceQuery workflowInstanceQuery, final int maxItems, final int skipCount) { if (workflowInstanceQuery.getWorkflowDefinitionId() == null && workflowInstanceQuery.getEngineId() == null) { List<String> ids = Arrays.asList(registry.getWorkflowComponents()); return CollectionUtils.transformFlat(ids, new Function<String, Collection<WorkflowInstance>>() { public List<WorkflowInstance> apply(String id) { WorkflowComponent component = registry.getWorkflowComponent(id); return component.getWorkflows(workflowInstanceQuery, maxItems, skipCount); } }); } String engineId = workflowInstanceQuery.getEngineId(); if (engineId == null) { engineId = BPMEngineRegistry.getEngineId(workflowInstanceQuery.getWorkflowDefinitionId()); } WorkflowComponent component = getWorkflowComponent(engineId); return component.getWorkflows(workflowInstanceQuery, maxItems, skipCount); } @Override public long countWorkflows(final WorkflowInstanceQuery workflowInstanceQuery) { // MNT-9074 My Tasks fails to render if tasks quantity is excessive if (workflowInstanceQuery.getWorkflowDefinitionId() == null && workflowInstanceQuery.getEngineId() == null) { List<String> ids = Arrays.asList(registry.getWorkflowComponents()); long total = 0; for (String id : ids) { WorkflowComponent component = registry.getWorkflowComponent(id); total += component.countWorkflows(workflowInstanceQuery); } return total; } String engineId = workflowInstanceQuery.getEngineId(); if (engineId == null) { engineId = BPMEngineRegistry.getEngineId(workflowInstanceQuery.getWorkflowDefinitionId()); } WorkflowComponent component = getWorkflowComponent(engineId); return component.countWorkflows(workflowInstanceQuery); } @Override public long countTasks(WorkflowTaskQuery workflowTaskQuery) { long total = 0; if (workflowTaskQuery.getEngineId() != null) { TaskComponent component = registry.getTaskComponent(workflowTaskQuery.getEngineId()); total = component.countTasks(workflowTaskQuery); } else { List<String> ids = Arrays.asList(registry.getTaskComponents()); for (String id : ids) { TaskComponent component = registry.getTaskComponent(id); total += component.countTasks(workflowTaskQuery); } } return total; } /** * {@inheritDoc} */ public List<WorkflowInstance> getActiveWorkflows() { return getWorkflows(new WorkflowInstanceQuery(true)); } /** * {@inheritDoc} */ public List<WorkflowInstance> getCompletedWorkflows() { return getWorkflows(new WorkflowInstanceQuery(false)); } /** * {@inheritDoc} */ public List<WorkflowInstance> getWorkflows() { return getWorkflows(new WorkflowInstanceQuery()); } /** * {@inheritDoc} */ public WorkflowInstance getWorkflowById(String workflowId) { String engineId = BPMEngineRegistry.getEngineId(workflowId); WorkflowComponent component = getWorkflowComponent(engineId); return component.getWorkflowById(workflowId); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getWorkflowPaths(java * .lang.String) */ public List<WorkflowPath> getWorkflowPaths(String workflowId) { String engineId = BPMEngineRegistry.getEngineId(workflowId); WorkflowComponent component = getWorkflowComponent(engineId); return component.getWorkflowPaths(workflowId); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getPathProperties(java * .lang.String) */ public Map<QName, Serializable> getPathProperties(String pathId) { String engineId = BPMEngineRegistry.getEngineId(pathId); WorkflowComponent component = getWorkflowComponent(engineId); return component.getPathProperties(pathId); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#cancelWorkflow(java * .lang.String) */ public WorkflowInstance cancelWorkflow(String workflowId) { return cancelWorkflows(Collections.singletonList(workflowId)).get(0); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#cancelWorkflows */ public List<WorkflowInstance> cancelWorkflows(List<String> workflowIds) { if (workflowIds == null) { workflowIds = Collections.emptyList(); } if (logger.isTraceEnabled()) { logger.trace("Cancelling " + workflowIds.size() + " workflowIds..."); } List<WorkflowInstance> result = new ArrayList<WorkflowInstance>(workflowIds.size()); // Batch the workflow IDs by engine ID Map<String, List<String>> workflows = batchByEngineId(workflowIds); // Process each engine's batch for (Map.Entry<String, List<String>> entry : workflows.entrySet()) { String engineId = entry.getKey(); WorkflowComponent component = getWorkflowComponent(engineId); List<WorkflowInstance> instances = component.cancelWorkflows(entry.getValue()); for (WorkflowInstance instance : instances) { // NOTE: Delete workflow package after cancelling workflow, so it's // still available // in process-end events of workflow definition workflowPackageComponent.deletePackage(instance.getWorkflowPackage()); result.add(instance); } } return result; } public void setMultiTenantWorkflowDeploymentEnabled(boolean deployWorkflowsInTenant) { this.deployWorkflowsInTenant = deployWorkflowsInTenant; } public boolean isMultiTenantWorkflowDeploymentEnabled() { return deployWorkflowsInTenant; } private Map<String, List<String>> batchByEngineId(List<String> workflowIds) { Map<String, List<String>> workflows = new LinkedHashMap<String, List<String>>(workflowIds.size() * 2); for (String workflowId : workflowIds) { String engineId = BPMEngineRegistry.getEngineId(workflowId); List<String> engineWorkflows = workflows.get(engineId); if (engineWorkflows == null) { engineWorkflows = new LinkedList<String>(); workflows.put(engineId, engineWorkflows); } engineWorkflows.add(workflowId); } return workflows; } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#deleteWorkflow(java * .lang.String) */ public WorkflowInstance deleteWorkflow(String workflowId) { String engineId = BPMEngineRegistry.getEngineId(workflowId); WorkflowComponent component = getWorkflowComponent(engineId); WorkflowInstance instance = component.deleteWorkflow(workflowId); // NOTE: Delete workflow package after deleting workflow, so it's still // available // in process-end events of workflow definition workflowPackageComponent.deletePackage(instance.getWorkflowPackage()); return instance; } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#signal(java.lang.String * , java.lang.String) */ public WorkflowPath signal(String pathId, String transition) { String engineId = BPMEngineRegistry.getEngineId(pathId); WorkflowComponent component = getWorkflowComponent(engineId); return component.signal(pathId, transition); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#fireEvent(java.lang * .String, java.lang.String) */ public WorkflowPath fireEvent(String pathId, String event) { String engineId = BPMEngineRegistry.getEngineId(pathId); WorkflowComponent component = getWorkflowComponent(engineId); return component.fireEvent(pathId, event); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getTimers(java.lang * .String) */ public List<WorkflowTimer> getTimers(String workflowId) { String engineId = BPMEngineRegistry.getEngineId(workflowId); WorkflowComponent component = getWorkflowComponent(engineId); return component.getTimers(workflowId); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getTasksForWorkflowPath * (java.lang.String) */ public List<WorkflowTask> getTasksForWorkflowPath(String pathId) { String engineId = BPMEngineRegistry.getEngineId(pathId); WorkflowComponent component = getWorkflowComponent(engineId); return component.getTasksForWorkflowPath(pathId); } /** * {@inheritDoc} */ @Override public WorkflowTask getStartTask(String workflowInstanceId) { String engineId = BPMEngineRegistry.getEngineId(workflowInstanceId); TaskComponent component = getTaskComponent(engineId); return component.getStartTask(workflowInstanceId); } @Override public List<WorkflowTask> getStartTasks(List<String> workflowInstanceIds, boolean sameSession) { List<WorkflowTask> result = new ArrayList<WorkflowTask>(workflowInstanceIds.size()); // Batch the workflow IDs by engine ID Map<String, List<String>> workflows = batchByEngineId(workflowInstanceIds); // Process each engine's batch for (Map.Entry<String, List<String>> entry : workflows.entrySet()) { String engineId = entry.getKey(); TaskComponent component = getTaskComponent(engineId); List<WorkflowTask> startTasks = component.getStartTasks(entry.getValue(), sameSession); // Optimization to allow 'lazy list' to pass through if (workflows.size() == 1) { return startTasks; } result.addAll(startTasks); } return result; } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#hasWorkflowImage( * java.lang.String) */ public boolean hasWorkflowImage(String workflowInstanceId) { String engineId = BPMEngineRegistry.getEngineId(workflowInstanceId); WorkflowComponent component = getWorkflowComponent(engineId); return component.hasWorkflowImage(workflowInstanceId); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getWorkflowImage( * java.lang.String) */ public InputStream getWorkflowImage(String workflowInstanceId) { String engineId = BPMEngineRegistry.getEngineId(workflowInstanceId); WorkflowComponent component = getWorkflowComponent(engineId); return component.getWorkflowImage(workflowInstanceId); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getAssignedTasks(java * .lang.String, org.alfresco.service.cmr.workflow.WorkflowTaskState) */ public List<WorkflowTask> getAssignedTasks(String authority, WorkflowTaskState state) { return getAssignedTasks(authority, state, false); } @Override public List<WorkflowTask> getAssignedTasks(String authority, WorkflowTaskState state, boolean lazyInitialization) { List<WorkflowTask> tasks = new ArrayList<WorkflowTask>(10); String[] ids = registry.getTaskComponents(); for (String id : ids) { TaskComponent component = registry.getTaskComponent(id); tasks.addAll(component.getAssignedTasks(authority, state, lazyInitialization)); } return Collections.unmodifiableList(tasks); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getPooledTasks(java * .lang.String) */ public List<WorkflowTask> getPooledTasks(String authority) { return getPooledTasks(authority, false); } @Override public List<WorkflowTask> getPooledTasks(String authority, boolean lazyinitialization) { // Expand authorities to include associated groups (and parent groups) List<String> authorities = new ArrayList<String>(); authorities.add(authority); Set<String> parents = authorityService.getContainingAuthorities(AuthorityType.GROUP, authority, false); authorities.addAll(parents); if (maxAuthoritiesForPooledTasks > 0 && authorities.size() > maxAuthoritiesForPooledTasks) { authorities = authorities.subList(0, maxAuthoritiesForPooledTasks); } // Retrieve pooled tasks for authorities (from each of the registered task components) List<WorkflowTask> tasks = new ArrayList<WorkflowTask>(10); int taskCount = 0; String[] ids = registry.getTaskComponents(); OUTER: for (String id : ids) { TaskComponent component = registry.getTaskComponent(id); for (int i = 0; i < authorities.size(); i += 1000) { List<WorkflowTask> pooledTasks = component.getPooledTasks( authorities.subList(i, Math.min(i + 1000, authorities.size())), lazyinitialization); // Clip the results if necessary if (maxPooledTasks > 0) { for (WorkflowTask pooledTask : pooledTasks) { tasks.add(pooledTask); if (++taskCount >= maxPooledTasks) { break OUTER; } } } else { tasks.addAll(pooledTasks); } } } 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) { return queryTasks(query, false); } public List<WorkflowTask> queryTasks(WorkflowTaskQuery query, boolean sameSession) { // extract task component to perform query String engineId = query.getEngineId(); String processId = query.getProcessId(); if (processId != null) { String workflowEngineId = BPMEngineRegistry.getEngineId(processId); if (engineId != null && !engineId.equals(workflowEngineId)) { throw new WorkflowException("Cannot query for tasks across multiple task components: " + engineId + ", " + workflowEngineId); } engineId = workflowEngineId; } 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; String[] ids = registry.getTaskComponents(); List<TaskComponent> taskComponents = new ArrayList<TaskComponent>(ids.length); 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; } taskComponents.add(component); } if (taskComponents.size() == 1) { tasks = taskComponents.get(0).queryTasks(query, sameSession); } else { tasks = new ArrayList<WorkflowTask>(10); for (TaskComponent component : taskComponents) { tasks.addAll(component.queryTasks(query, sameSession)); } } return Collections.unmodifiableList(tasks); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#updateTask(java.lang * .String, java.util.Map, java.util.Map, java.util.Map) */ public WorkflowTask updateTask(String taskId, Map<QName, Serializable> properties, Map<QName, List<NodeRef>> add, Map<QName, List<NodeRef>> remove) { String engineId = BPMEngineRegistry.getEngineId(taskId); TaskComponent component = getTaskComponent(engineId); // get the current assignee before updating the task String originalAsignee = (String) component.getTaskById(taskId).getProperties() .get(ContentModel.PROP_OWNER); WorkflowTask task = component.updateTask(taskId, properties, add, remove); if (add != null && add.containsKey(WorkflowModel.ASSOC_PACKAGE)) { WorkflowInstance instance = task.getPath().getInstance(); workflowPackageComponent.setWorkflowForPackage(instance); } // Get the 'new' assignee String assignee = (String) properties.get(ContentModel.PROP_OWNER); if (assignee != null && assignee.length() != 0) { // if the assignee has changed get the start task if (!assignee.equals(originalAsignee)) { String instanceId = task.getPath().getInstance().getId(); WorkflowTask startTask = component.getStartTask(instanceId); if (startTask != null) { // Get the email notification flag Boolean sendEMailNotification = (Boolean) startTask.getProperties() .get(WorkflowModel.PROP_SEND_EMAIL_NOTIFICATIONS); if (Boolean.TRUE.equals(sendEMailNotification) == true) { // calculate task type label String workflowDefId = task.getPath().getInstance().getDefinition().getName(); if (workflowDefId.indexOf('$') != -1 && (workflowDefId.indexOf('$') < workflowDefId.length() - 1)) { workflowDefId = workflowDefId.substring(workflowDefId.indexOf('$') + 1); } // Send the notification workflowNotificationUtils.sendWorkflowAssignedNotificationEMail(taskId, null, assignee, false); } } } } return task; } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#endTask(java.lang.String * , java.lang.String) */ public WorkflowTask endTask(String taskId, String transition) { String engineId = BPMEngineRegistry.getEngineId(taskId); TaskComponent component = getTaskComponent(engineId); return component.endTask(taskId, transition); } /* * @see org.alfresco.service.cmr.workflow.WorkflowService#isTaskEditable(org.alfresco.service.cmr.workflow.WorkflowTask, java.lang.String) */ public boolean isTaskEditable(WorkflowTask task, String username) { return isTaskEditable(task, username, true); } @Override public boolean isTaskEditable(WorkflowTask task, String username, boolean refreshTask) { if (!transactionService.getAllowWrite()) { return false; } if (refreshTask) { task = getTaskById(task.getId()); // Refresh the task. } // if task is null just return false if (task == null) { return false; } // if the task is complete it is not editable if (task.getState() == WorkflowTaskState.COMPLETED) { return false; } if (isUserOwnerOrInitiator(task, username)) { // editable if the current user is the task owner or initiator return true; } if (task.getProperties().get(ContentModel.PROP_OWNER) == null) { // if the user is not the owner or initiator check whether they are // a member of the pooled actors for the task (if it has any) return isUserInPooledActors(task, username); } else { // if the task has an owner and the user is not the owner // or the initiator do not allow editing return false; } } /* * @see org.alfresco.service.cmr.workflow.WorkflowService#isTaskReassignable(org.alfresco.service.cmr.workflow.WorkflowTask, java.lang.String) */ public boolean isTaskReassignable(WorkflowTask task, String username) { return isTaskReassignable(task, username, true); } @Override public boolean isTaskReassignable(WorkflowTask task, String username, boolean refreshTask) { if (!transactionService.getAllowWrite()) { return false; } if (refreshTask) { task = getTaskById(task.getId()); // Refresh the task. } // if task is null just return false if (task == null) { return false; } // if the task is complete it is not reassignable if (task.getState() == WorkflowTaskState.COMPLETED) { return false; } // if a task does not have an owner it can not be reassigned if (task.getProperties().get(ContentModel.PROP_OWNER) == null) { return false; } // if the task has the 'reassignable' property set to false it can not be reassigned Map<QName, Serializable> properties = task.getProperties(); Boolean reassignable = (Boolean) properties.get(WorkflowModel.PROP_REASSIGNABLE); if (reassignable != null && reassignable.booleanValue() == false) { return false; } // if the task has pooled actors and an owner it can not be reassigned (it must be released) Collection<?> actors = (Collection<?>) properties.get(WorkflowModel.ASSOC_POOLED_ACTORS); String owner = (String) properties.get(ContentModel.PROP_OWNER); if (actors != null && !actors.isEmpty() && owner != null) { return false; } if (isUserOwnerOrInitiator(task, username)) { // reassignable if the current user is the task owner or initiator return true; } return false; } /* * @see org.alfresco.service.cmr.workflow.WorkflowService#isTaskClaimable(org.alfresco.service.cmr.workflow.WorkflowTask, java.lang.String) */ public boolean isTaskClaimable(WorkflowTask task, String username) { return isTaskClaimable(task, username, true); } @Override public boolean isTaskClaimable(WorkflowTask task, String username, boolean refreshTask) { if (!transactionService.getAllowWrite()) { return false; } if (refreshTask) { task = getTaskById(task.getId()); // Refresh the task. } // if task is null just return false if (task == null) { return false; } // if the task is complete it is not claimable if (task.getState() == WorkflowTaskState.COMPLETED) { return false; } // if the task has an owner it can not be claimed if (task.getProperties().get(ContentModel.PROP_OWNER) != null) { return false; } // a task can only be claimed if the user is a member of // of the pooled actors for the task return isUserInPooledActors(task, username); } /* * @see org.alfresco.service.cmr.workflow.WorkflowService#isTaskReleasable(org.alfresco.service.cmr.workflow.WorkflowTask, java.lang.String) */ public boolean isTaskReleasable(WorkflowTask task, String username) { return isTaskReleasable(task, username, true); } @Override public boolean isTaskReleasable(WorkflowTask task, String username, boolean refreshTask) { if (!transactionService.getAllowWrite()) { return false; } if (refreshTask) { task = getTaskById(task.getId()); // Refresh the task. } // if task is null just return false if (task == null) { return false; } // if the task is complete it is not releasable if (task.getState() == WorkflowTaskState.COMPLETED) { return false; } // if the task doesn't have pooled actors it is not releasable Map<QName, Serializable> properties = task.getProperties(); Collection<?> actors = (Collection<?>) properties.get(WorkflowModel.ASSOC_POOLED_ACTORS); if (actors == null || actors.isEmpty()) { return false; } // if the task does not have an owner it is not releasable String owner = (String) properties.get(ContentModel.PROP_OWNER); if (owner == null) { return false; } if (isUserOwnerOrInitiator(task, username)) { // releasable if the current user is the task owner or initiator return true; } return false; } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getTaskById(java.lang * .String) */ public WorkflowTask getTaskById(String taskId) { String engineId = BPMEngineRegistry.getEngineId(taskId); TaskComponent component = getTaskComponent(engineId); return component.getTaskById(taskId); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#createPackage(java. * lang.String, org.alfresco.service.cmr.repository.NodeRef) */ public NodeRef createPackage(NodeRef container) { return workflowPackageComponent.createPackage(container); } /* * (non-Javadoc) * @see * org.alfresco.service.cmr.workflow.WorkflowService#getWorkflowsForContent * (org.alfresco.service.cmr.repository.NodeRef, boolean) */ public List<WorkflowInstance> getWorkflowsForContent(NodeRef packageItem, boolean active) { List<String> workflowIds = workflowPackageComponent.getWorkflowIdsForContent(packageItem); List<WorkflowInstance> workflowInstances = new ArrayList<WorkflowInstance>(workflowIds.size()); for (String workflowId : workflowIds) { String engineId = BPMEngineRegistry.getEngineId(workflowId); WorkflowComponent component = getWorkflowComponent(engineId); WorkflowInstance instance = component.getWorkflowById(workflowId); if (instance != null && instance.isActive() == active) { workflowInstances.add(instance); } } return workflowInstances; } /** * Gets the Workflow Component registered against the specified BPM Engine * Id * * @param engineId engine id */ private WorkflowComponent getWorkflowComponent(String engineId) { WorkflowComponent component = registry.getWorkflowComponent(engineId); if (component == null) { throw new WorkflowException("Workflow Component for engine id '" + engineId + "' is not registered"); } return component; } /** * Gets the Task Component registered against the specified BPM Engine Id * * @param engineId engine id */ private TaskComponent getTaskComponent(String engineId) { TaskComponent component = registry.getTaskComponent(engineId); if (component == null) { throw new WorkflowException("Task Component for engine id '" + engineId + "' is not registered"); } return component; } public List<NodeRef> getPackageContents(String taskId) { NodeRef workflowPackage = getWorkflowPackageIfExists(taskId); if (workflowPackage == null) { return Collections.emptyList(); } else { return getPackageContents(workflowPackage); } } public List<NodeRef> getPackageContents(NodeRef packageRef) { return getRepositoryPackageContents(packageRef); } /** * Attempts to get the workflow package node from the workflow task * specified by the task Id. If the task Id is invalid or no workflow * package is associated with the specified task then this method returns * null. * * @param taskId String * @return The workflow package NodeRef or null. */ private NodeRef getWorkflowPackageIfExists(String taskId) { WorkflowTask workflowTask = getTaskById(taskId); if (workflowTask != null) { return (NodeRef) workflowTask.getProperties().get(WorkflowModel.ASSOC_PACKAGE); } return null; } /** * @param workflowPackage NodeRef */ private List<NodeRef> getRepositoryPackageContents(NodeRef workflowPackage) { List<NodeRef> contents = new ArrayList<NodeRef>(); // get existing workflow package items List<ChildAssociationRef> packageAssocs = protectedNodeService.getChildAssocs(workflowPackage); for (ChildAssociationRef assoc : packageAssocs) { // create our Node representation from the NodeRef NodeRef nodeRef = assoc.getChildRef(); QName assocType = assoc.getTypeQName(); if (!protectedNodeService.exists(nodeRef)) { if (logger.isDebugEnabled()) logger.debug("Ignoring " + nodeRef + " as it has been removed from the repository"); } else if (!ContentModel.ASSOC_CONTAINS.equals(assocType) && !WorkflowModel.ASSOC_PACKAGE_CONTAINS.equals(assocType)) { if (logger.isDebugEnabled()) logger.debug("Ignoring " + nodeRef + " as it has an invalid association type: " + assocType); } else { if (checkTypeIsInDataDictionary(nodeRef)) { contents.add(nodeRef); } } } return contents; } /** * Gets teh type of the nodeRef and checks that the type exists in the data * dcitionary. If the type is not in the data dictionary then the method * logs a warning and returns false. Otherwise it returns true. * * @param nodeRef NodeRef * @return True if the nodeRef type is in the data dictionary, otherwise * false. */ private boolean checkTypeIsInDataDictionary(NodeRef nodeRef) { QName type = protectedNodeService.getType(nodeRef); if (dictionaryService.getType(type) == null) { if (logger.isWarnEnabled()) logger.warn("Found invalid object in database: id = " + nodeRef + ", type = " + type); return false; } return true; } /** * Determines if the given user is a member of the pooled actors assigned to the task * * @param task The task instance to check * @param username The username to check * @return true if the user is a pooled actor, false otherwise */ @SuppressWarnings("unchecked") private boolean isUserInPooledActors(WorkflowTask task, String username) { // Get the pooled actors Collection<NodeRef> actors = (Collection<NodeRef>) task.getProperties() .get(WorkflowModel.ASSOC_POOLED_ACTORS); if (actors != null) { for (NodeRef actor : actors) { QName type = nodeService.getType(actor); if (dictionaryService.isSubClass(type, ContentModel.TYPE_PERSON)) { Serializable name = nodeService.getProperty(actor, ContentModel.PROP_USERNAME); if (name != null && name.equals(username)) { return true; } } else if (dictionaryService.isSubClass(type, ContentModel.TYPE_AUTHORITY_CONTAINER)) { if (isUserInGroup(username, actor)) { // The user is a member of the group return true; } } } } return false; } private boolean isUserInGroup(String username, NodeRef group) { // Get the group name String name = (String) nodeService.getProperty(group, ContentModel.PROP_AUTHORITY_NAME); // Get all group members Set<String> groupMembers = authorityService.getContainedAuthorities(AuthorityType.USER, name, false); // Chekc if the user is a group member. return groupMembers != null && groupMembers.contains(username); } /** * Determines if the given user is the owner of the given task or * the initiator of the workflow the task is part of * * @param task The task to check * @param username The username to check * @return true if the user is the owner or the workflow initiator */ private boolean isUserOwnerOrInitiator(WorkflowTask task, String username) { boolean result = false; String owner = (String) task.getProperties().get(ContentModel.PROP_OWNER); if (username.equals(owner)) { // user owns the task result = true; } else if (username.equals(getWorkflowInitiatorUsername(task))) { // user is the workflow initiator result = true; } return result; } /** * Returns the username of the user that initiated the workflow the * given task is part of. * * @param task The task to get the workflow initiator for * @return Username or null if the initiator could not be found */ private String getWorkflowInitiatorUsername(WorkflowTask task) { String initiator = null; NodeRef initiatorRef = task.getPath().getInstance().getInitiator(); if (initiatorRef != null && this.nodeService.exists(initiatorRef)) { initiator = (String) this.nodeService.getProperty(initiatorRef, ContentModel.PROP_USERNAME); } return initiator; } }