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.jbpm; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.util.AbstractList; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.Map.Entry; import java.util.zip.ZipInputStream; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authority.AuthorityDAO; import org.alfresco.repo.workflow.AlfrescoBpmEngine; import org.alfresco.repo.workflow.BPMEngineRegistry; import org.alfresco.repo.workflow.WorkflowConstants; import org.alfresco.repo.workflow.WorkflowEngine; import org.alfresco.repo.workflow.WorkflowModel; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.PropertyDefinition; 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; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.PersonService; 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.WorkflowNode; import org.alfresco.service.cmr.workflow.WorkflowPath; 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.cmr.workflow.WorkflowTransition; import org.alfresco.service.cmr.workflow.WorkflowInstanceQuery.DatePosition; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; import org.alfresco.util.collections.CollectionUtils; import org.alfresco.util.collections.Function; import org.apache.commons.lang.time.DateUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; 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; import org.hibernate.criterion.Disjunction; import org.hibernate.criterion.Order; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Property; import org.hibernate.criterion.Restrictions; import org.hibernate.proxy.HibernateProxy; import org.jbpm.JbpmContext; import org.jbpm.JbpmException; import org.jbpm.context.exe.ContextInstance; import org.jbpm.context.exe.TokenVariableMap; import org.jbpm.context.exe.VariableInstance; import org.jbpm.context.exe.converter.BooleanToStringConverter; import org.jbpm.context.exe.variableinstance.DateInstance; import org.jbpm.context.exe.variableinstance.LongInstance; import org.jbpm.context.exe.variableinstance.NullInstance; import org.jbpm.context.exe.variableinstance.StringInstance; import org.jbpm.db.GraphSession; import org.jbpm.db.TaskMgmtSession; import org.jbpm.file.def.FileDefinition; import org.jbpm.graph.def.Event; import org.jbpm.graph.def.Node; import org.jbpm.graph.def.ProcessDefinition; import org.jbpm.graph.def.Transition; import org.jbpm.graph.exe.Comment; import org.jbpm.graph.exe.ExecutionContext; import org.jbpm.graph.exe.ProcessInstance; import org.jbpm.graph.exe.Token; import org.jbpm.job.Timer; import org.jbpm.jpdl.par.ProcessArchive; import org.jbpm.jpdl.xml.Problem; import org.jbpm.taskmgmt.def.Task; import org.jbpm.taskmgmt.def.TaskMgmtDefinition; import org.jbpm.taskmgmt.exe.PooledActor; import org.jbpm.taskmgmt.exe.TaskInstance; import org.springframework.dao.DataAccessException; import org.springframework.util.StringUtils; import org.springmodules.workflow.jbpm31.JbpmAccessor; import org.springmodules.workflow.jbpm31.JbpmCallback; import org.springmodules.workflow.jbpm31.JbpmTemplate; /** * JBoss JBPM based implementation of: * * Workflow Definition Component Workflow Component Task Component * * @author davidc */ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine { private static Log logger = LogFactory.getLog(JBPMEngine.class); // Implementation dependencies protected NodeService nodeService; protected ServiceRegistry serviceRegistry; protected PersonService personService; protected AuthorityDAO authorityDAO; protected JbpmTemplate jbpmTemplate; protected SearchService unprotectedSearchService; // Company Home protected StoreRef companyHomeStore; protected String companyHomePath; private Map<String, QName> ignoredProperties = new HashMap<String, QName>(3); // Note: jBPM query which is not provided out-of-the-box // TODO: Check jBPM 3.2 and get this implemented in jBPM private final static String COMPLETED_TASKS_QUERY = "select ti " + "from org.jbpm.taskmgmt.exe.TaskInstance as ti " + "where ti.actorId = :actorId " + "and ti.isOpen = false " + "and ti.end is not null"; // Note: jBPM query which is not provided out-of-the-box // TODO: Check jBPMg future and get this implemented in jBPM private final static String PROCESS_TIMERS_QUERY = "select timer " + "from org.jbpm.job.Timer timer " + "where timer.processInstance = :process "; // Workflow Path Seperators private final static String WORKFLOW_PATH_SEPERATOR = "-"; private final static String WORKFLOW_TOKEN_SEPERATOR = "@"; // I18N labels private final static String TITLE_LABEL = "title"; private final static String DESC_LABEL = "description"; private final static String DEFAULT_TRANSITION_LABEL = "bpm_businessprocessmodel.transition"; private static final String ERR_MANDATORY_TASK_PROPERTIES_MISSING = "Jbpm.engine.mandatory.properties.missing"; private static final String ERR_DEPLOY_WORKFLOW = "jbpm.engine.deploy.workflow.error"; private static final String ERR_IS_WORKFLOW_DEPLOYED = "jbpm.engine.is.workflow.deployed.error"; private static final String ERR_UNDEPLOY_WORKFLOW = "jbpm.engine.undeploy.workflow.error"; private static final String ERR_GET_WORKFLOW_DEF = "jbpm.engine.get.workflow.definition.error"; private static final String ERR_GET_WORKFLOW_DEF_BY_ID = "jbpm.engine.get.workflow.definition.by.id.error"; private static final String ERR_GET_WORKFLOW_DEF_BY_NAME = "jbpm.engine.get.workflow.definition.by.name.error"; private static final String ERR_GET_ALL_DEFS_BY_NAME = "jbpm.engine.get.all.workflow.definitions.by.name.error"; private static final String ERR_GET_DEF_IMAGE = "jbpm.engine.get.workflow.definition.image.error"; private static final String ERR_GET_TASK_DEFS = "jbpm.engine.get.task.definitions.error"; private static final String ERR_GET_PROCESS_DEF = "jbpm.engine.get.process.definition.error"; private static final String ERR_START_WORKFLOW = "jbpm.enginestart.workflow.error"; private static final String ERR_GET_ACTIVE_WORKFLOW_INSTS = "jbpm.engine.get.active.workflows.error"; private static final String ERR_GET_WORKFLOW_INST_BY_ID = "jbpm.engine.get.workflow.instance.by.id.error"; private static final String ERR_GET_PROCESS_INSTANCE = "jbpm.engine.get.process.instance.error"; private static final String ERR_GET_WORKFLOW_PATHS = "jbpm.engine.get.workflow.paths.error"; private static final String ERR_GET_PATH_PROPERTIES = "jbpm.engine.get.path.properties.error"; private static final String ERR_CANCEL_WORKFLOW = "jbpm.engine.cancel.workflow.error"; private static final String ERR_DELETE_WORKFLOW = "jbpm.engine.delete.workflow.error"; private static final String ERR_SIGNAL_TRANSITION = "jbpm.engine.signal.transition.error"; protected static final String ERR_INVALID_EVENT = "jbpm.engine.invalid.event"; private static final String ERR_FIRE_EVENT = "jbpm.engine.fire.event.error"; private static final String ERR_GET_TASKS_FOR_PATH = "jbpm.engine.get.tasks.for.path.error"; private static final String ERR_GET_TIMERS = "jbpm.engine.get.timers.error"; protected static final String ERR_FIND_COMPLETED_TASK_INSTS = "jbpm.engine.find.completed.task.instances.error"; private static final String ERR_GET_ASSIGNED_TASKS = "jbpm.engine.get.assigned.tasks.error"; private static final String ERR_GET_POOLED_TASKS = "jbpm.engine.get.pooled.tasks.error"; private static final String ERR_QUERY_TASKS = "jbpm.engine.query.tasks.error"; private static final String ERR_GET_TASK_INST = "jbpm.engine.get.task.instance.error"; private static final String ERR_UPDATE_TASK = "jbpm.engine.update.task.error"; protected static final String ERR_END_TASK_INVALID_TRANSITION = "jbpm.engine.end.task.invalid.transition"; private static final String ERR_END_TASK = "jbpm.engine.end.task.error"; private static final String ERR_GET_TASK_BY_ID = "jbpm.engine.get.task.by.id.error"; private static final String ERR_GET_START_TASK = "jbpm.engine.get.start.task.error"; private static final String ERR_COMPILE_PROCESS_DEF_zip = "jbpm.engine.compile.process.definition.zip.error"; private static final String ERR_COMPILE_PROCESS_DEF_XML = "jbpm.engine.compile.process.definition.xml.error"; private static final String ERR_COMPILE_PROCESS_DEF_UNSUPPORTED = "jbpm.engine.compile.process.definition.unsupported.error"; private static final String ERR_GET_JBPM_ID = "jbpm.engine.get.jbpm.id.error"; private static final String ERR_GET_WORKFLOW_TOKEN_INVALID = "jbpm.engine.get.workflow.token.invalid"; private static final String ERR_GET_WORKFLOW_TOKEN_NULL = "jbpm.engine.get.workflow.token.is.null"; private static final String ERR_SET_TASK_PROPS_INVALID_VALUE = "jbpm.engine.set.task.properties.invalid.value"; private static final String ERR_CONVERT_VALUE = "jbpm.engine.convert.value.error"; private static final String ERR_GET_COMPANY_HOME_INVALID = "jbpm.engine.get.company.home.invalid"; private static final String ERR_GET_COMPANY_HOME_MULTIPLE = "jbpm.engine.get.company.home.multiple"; // engine ID public static final String ENGINE_ID = "jbpm"; public JBPMEngine() { super(); ignoredProperties.put(WorkflowModel.PROP_STATUS.getLocalName(), WorkflowModel.PROP_STATUS); ignoredProperties.put(WorkflowModel.PROP_PACKAGE_ITEM_ACTION_GROUP.getLocalName(), WorkflowModel.PROP_PACKAGE_ITEM_ACTION_GROUP); ignoredProperties.put(WorkflowModel.PROP_PACKAGE_ACTION_GROUP.getLocalName(), WorkflowModel.PROP_PACKAGE_ACTION_GROUP); } /** * Sets the JBPM Template used for accessing JBoss JBPM in the correct * context * * @param jbpmTemplate JbpmTemplate */ public void setJBPMTemplate(JbpmTemplate jbpmTemplate) { this.jbpmTemplate = jbpmTemplate; } /** * Sets the Node Service * * @param nodeService NodeService */ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } /** * Sets the Person Service * * @param personService PersonService */ public void setPersonService(PersonService personService) { this.personService = personService; } /** * Sets the Authority DAO * * @param authorityDAO AuthorityDAO */ public void setAuthorityDAO(AuthorityDAO authorityDAO) { this.authorityDAO = authorityDAO; } /** * Sets the Service Registry * * @param serviceRegistry ServiceRegistry */ public void setServiceRegistry(ServiceRegistry serviceRegistry) { this.serviceRegistry = serviceRegistry; } /** * Sets the Company Home Path * * @param companyHomePath String */ public void setCompanyHomePath(String companyHomePath) { this.companyHomePath = companyHomePath; } /** * Sets the Company Home Store * * @param companyHomeStore String */ public void setCompanyHomeStore(String companyHomeStore) { this.companyHomeStore = new StoreRef(companyHomeStore); } /** * Set the unprotected search service - so we can find the node ref for * company home when folk do not have read access to company home TODO: * review use with DC * * @param unprotectedSearchService SearchService */ public void setUnprotectedSearchService(SearchService unprotectedSearchService) { this.unprotectedSearchService = unprotectedSearchService; } // // Workflow Definition... // /* * @see org.alfresco.repo.workflow.WorkflowComponent#deployDefinition(java.io.InputStream, java.lang.String) */ public WorkflowDeployment deployDefinition(final InputStream workflowDefinition, final String mimetype) { return deployDefinition(workflowDefinition, mimetype, null); } /* * @see org.alfresco.repo.workflow.WorkflowComponent#deployDefinition(java.io.InputStream, java.lang.String, java.lang.String) */ public WorkflowDeployment deployDefinition(final InputStream workflowDefinition, final String mimetype, String name) { try { return (WorkflowDeployment) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { // construct process definition CompiledProcessDefinition compiledDef = compileProcessDefinition(workflowDefinition, mimetype); // deploy the parsed definition context.deployProcessDefinition(compiledDef.def); // return deployed definition WorkflowDeployment workflowDeployment = createWorkflowDeployment(compiledDef); return workflowDeployment; } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_DEPLOY_WORKFLOW); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowDefinitionComponent#isDefinitionDeployed * (java.io.InputStream, java.lang.String) */ public boolean isDefinitionDeployed(final InputStream workflowDefinition, final String mimetype) { try { return (Boolean) jbpmTemplate.execute(new JbpmCallback() { public Boolean doInJbpm(JbpmContext context) { // create process definition from input stream CompiledProcessDefinition processDefinition = compileProcessDefinition(workflowDefinition, mimetype); // retrieve process definition from Alfresco Repository GraphSession graphSession = context.getGraphSession(); String definitionName = processDefinition.def.getName(); ProcessDefinition existingDefinition = graphSession.findLatestProcessDefinition(definitionName); return existingDefinition != null; } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_IS_WORKFLOW_DEPLOYED); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowDefinitionComponent#undeployDefinition * (java.lang.String) */ public void undeployDefinition(final String workflowDefinitionId) { try { jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { // retrieve process definition GraphSession graphSession = context.getGraphSession(); ProcessDefinition processDefinition = getProcessDefinition(graphSession, workflowDefinitionId); // undeploy // NOTE: jBPM deletes all "in-flight" processes too // TODO: Determine if there's a safer undeploy we can expose // via the WorkflowService contract graphSession.deleteProcessDefinition(processDefinition); // we're done return null; } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_UNDEPLOY_WORKFLOW, workflowDefinitionId); throw new WorkflowException(msg, e); } } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<WorkflowDefinition> getDefinitions() { try { return (List<WorkflowDefinition>) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { GraphSession graphSession = context.getGraphSession(); List<ProcessDefinition> processDefs = graphSession.findLatestProcessDefinitions(); return getValidDefinitions(processDefs); } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_WORKFLOW_DEF); throw new WorkflowException(msg, e); } } private List<WorkflowDefinition> getValidDefinitions(Collection<ProcessDefinition> definitions) { List<ProcessDefinition> filteredDefs = factory.filterByDomain(definitions, new Function<ProcessDefinition, String>() { public String apply(ProcessDefinition definition) { return definition.getName(); } }); return convertDefinitions(filteredDefs); } private List<WorkflowDefinition> convertDefinitions(Collection<ProcessDefinition> definitions) { return CollectionUtils.transform(definitions, new Function<ProcessDefinition, WorkflowDefinition>() { public WorkflowDefinition apply(ProcessDefinition value) { try { return createWorkflowDefinition(value); } catch (Exception ex) { logger.warn("Unable to load workflow definition: '" + value + "' due to exception.", ex); return null; } } }); } private List<WorkflowInstance> convertWorkflows(Collection<ProcessInstance> instances) { return CollectionUtils.transform(instances, new Function<ProcessInstance, WorkflowInstance>() { public WorkflowInstance apply(ProcessInstance value) { try { return createWorkflowInstance(value); } catch (Exception ex) { logger.warn("Unable to load workflow instance: '" + value + "' due to exception.", ex); return null; } } }); } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowDefinitionComponent#getDefinitions() */ @SuppressWarnings("unchecked") public List<WorkflowDefinition> getAllDefinitions() { try { return (List<WorkflowDefinition>) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { GraphSession graphSession = context.getGraphSession(); List<ProcessDefinition> processDefs = graphSession.findAllProcessDefinitions(); return getValidDefinitions(processDefs); } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_WORKFLOW_DEF); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowDefinitionComponent#getDefinitionById * (java.lang.String) */ public WorkflowDefinition getDefinitionById(final String workflowDefinitionId) { try { return (WorkflowDefinition) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { // retrieve process GraphSession graphSession = context.getGraphSession(); ProcessDefinition processDefinition = getProcessDefinition(graphSession, workflowDefinitionId); return createWorkflowDefinition(processDefinition); } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_WORKFLOW_DEF_BY_ID, workflowDefinitionId); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowComponent#getDefinitionByName(java * .lang.String) */ public WorkflowDefinition getDefinitionByName(final String workflowName) { try { return (WorkflowDefinition) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { GraphSession graphSession = context.getGraphSession(); String definitionName = tenantService.getName(createLocalId(workflowName)); ProcessDefinition processDef = graphSession.findLatestProcessDefinition(definitionName); return processDef == null ? null : createWorkflowDefinition(processDef); } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_WORKFLOW_DEF_BY_NAME, workflowName); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowComponent#getAllDefinitionsByName( * java.lang.String) */ @SuppressWarnings("unchecked") public List<WorkflowDefinition> getAllDefinitionsByName(final String workflowName) { try { return (List<WorkflowDefinition>) jbpmTemplate.execute(new JbpmCallback() { @SuppressWarnings("synthetic-access") public Object doInJbpm(JbpmContext context) { GraphSession graphSession = context.getGraphSession(); String definitionName = tenantService.getName(createLocalId(workflowName)); List<ProcessDefinition> processDefs = graphSession .findAllProcessDefinitionVersions(definitionName); return convertDefinitions(processDefs); } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_ALL_DEFS_BY_NAME, workflowName); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowComponent#getDefinitionImage(java. * lang.String) */ public byte[] getDefinitionImage(final String workflowDefinitionId) { try { return (byte[]) jbpmTemplate.execute(new JbpmCallback() { @SuppressWarnings("synthetic-access") public Object doInJbpm(JbpmContext context) { GraphSession graphSession = context.getGraphSession(); ProcessDefinition processDefinition = getProcessDefinition(graphSession, workflowDefinitionId); FileDefinition fileDefinition = processDefinition.getFileDefinition(); return (fileDefinition == null) ? null : fileDefinition.getBytes("processimage.jpg"); } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_DEF_IMAGE, workflowDefinitionId); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowComponent#getAllTaskDefinitions(java * .lang.String) */ @SuppressWarnings("unchecked") public List<WorkflowTaskDefinition> getTaskDefinitions(final String workflowDefinitionId) { try { return (List<WorkflowTaskDefinition>) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { // retrieve process GraphSession graphSession = context.getGraphSession(); ProcessDefinition processDefinition = getProcessDefinition(graphSession, workflowDefinitionId); if (processDefinition == null) { return null; } else { String processName = processDefinition.getName(); if (tenantService.isEnabled()) { tenantService.checkDomain(processName); // throws // exception // if domain // mismatch } TaskMgmtDefinition taskMgmtDef = processDefinition.getTaskMgmtDefinition(); List<WorkflowTaskDefinition> workflowTaskDefs = new ArrayList<WorkflowTaskDefinition>(); for (Object task : taskMgmtDef.getTasks().values()) { workflowTaskDefs.add(createWorkflowTaskDefinition((Task) task)); } return (workflowTaskDefs.size() == 0) ? null : workflowTaskDefs; } } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_TASK_DEFS, workflowDefinitionId); throw new WorkflowException(msg, e); } } /** * Gets a jBPM process definition * * @param graphSession * jBPM graph session * @param workflowDefinitionId * workflow definition id * @return process definition */ protected ProcessDefinition getProcessDefinition(GraphSession graphSession, String workflowDefinitionId) { ProcessDefinition processDefinition = graphSession.getProcessDefinition(getJbpmId(workflowDefinitionId)); if ((processDefinition != null) && (tenantService.isEnabled())) { try { tenantService.checkDomain(processDefinition.getName()); // throws // exception // if // domain // mismatch } catch (RuntimeException re) { processDefinition = null; } } if (processDefinition == null) { String msg = messageService.getMessage(ERR_GET_PROCESS_DEF, workflowDefinitionId); throw new WorkflowException(msg); } return processDefinition; } // // Workflow Instance Management... // /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowComponent#startWorkflow(java.lang. * String, java.util.Map) */ public WorkflowPath startWorkflow(final String workflowDefinitionId, final Map<QName, Serializable> parameters) { try { return (WorkflowPath) jbpmTemplate.execute(new JbpmCallback() { @SuppressWarnings("synthetic-access") public Object doInJbpm(JbpmContext context) { // initialise jBPM actor (for any processes that wish to // record the initiator) String currentUserName = AuthenticationUtil.getFullyAuthenticatedUser(); context.setActorId(currentUserName); // construct a new process GraphSession graphSession = context.getGraphSession(); ProcessDefinition processDefinition = getProcessDefinition(graphSession, workflowDefinitionId); ProcessInstance processInstance = new ProcessInstance(processDefinition); processInstance.setKey(GUID.generate()); // assign initial process context ContextInstance processContext = processInstance.getContextInstance(); processContext.setVariable(WorkflowConstants.PROP_CANCELLED, false); if (parameters != null) { Serializable packageNode = parameters.get(WorkflowModel.ASSOC_PACKAGE); if (packageNode != null) { String pckgName = factory.mapQNameToName(WorkflowModel.ASSOC_PACKAGE); processContext.setVariable(pckgName, new JBPMNode((NodeRef) packageNode, serviceRegistry)); } } NodeRef companyHome = getCompanyHome(); processContext.setVariable(WorkflowConstants.PROP_COMPANY_HOME, new JBPMNode(companyHome, serviceRegistry)); NodeRef initiatorPerson = mapNameToPerson(currentUserName); if (initiatorPerson != null) { processContext.setVariable(WorkflowConstants.PROP_INITIATOR, new JBPMNode(initiatorPerson, serviceRegistry)); NodeRef initiatorHome = (NodeRef) nodeService.getProperty(initiatorPerson, ContentModel.PROP_HOMEFOLDER); if (initiatorHome != null) { processContext.setVariable(WorkflowConstants.PROP_INITIATOR_HOME, new JBPMNode(initiatorHome, serviceRegistry)); } } processContext.setVariable(WorkflowConstants.PROP_WORKFLOW_INSTANCE_ID, createGlobalId(new Long(processInstance.getId()).toString())); // create the start task if one exists Token token = processInstance.getRootToken(); Task startTask = processInstance.getTaskMgmtInstance().getTaskMgmtDefinition().getStartTask(); if (startTask != null) { TaskInstance taskInstance = processInstance.getTaskMgmtInstance().createStartTaskInstance(); setTaskProperties(taskInstance, parameters); token = taskInstance.getToken(); } // Save the process instance along with the task instance context.save(processInstance); return createWorkflowPath(token); } }); } catch (JbpmException e) { throw getStartWorkflowException(workflowDefinitionId, e); } catch (DataAccessException e) { throw getStartWorkflowException(workflowDefinitionId, e); } } private WorkflowException getStartWorkflowException(final String workflowDefinitionId, Exception e) { String msg = messageService.getMessage(ERR_START_WORKFLOW, workflowDefinitionId); return new WorkflowException(msg, e); } /** * {@inheritDoc} */ public List<WorkflowInstance> getActiveWorkflows() { return getWorkflows(new WorkflowInstanceQuery(true)); } /** * {@inheritDoc} */ @Override public List<WorkflowInstance> getCompletedWorkflows() { return getWorkflows(new WorkflowInstanceQuery(false)); } /** * {@inheritDoc} */ @Override public List<WorkflowInstance> getWorkflows() { return getWorkflows(new WorkflowInstanceQuery()); } /** * {@inheritDoc} */ public List<WorkflowInstance> getActiveWorkflows(final String workflowDefinitionId) { return getWorkflows(new WorkflowInstanceQuery(workflowDefinitionId, true)); } /** * {@inheritDoc} */ public List<WorkflowInstance> getCompletedWorkflows(final String workflowDefinitionId) { return getWorkflows(new WorkflowInstanceQuery(workflowDefinitionId, false)); } /** * {@inheritDoc} */ public List<WorkflowInstance> getWorkflows(final 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 query, int maxItems, int skipCount) { // MNT-9074 My Tasks fails to render if tasks quantity is excessive // here don't use maxItems and skipCount try { List<ProcessInstance> instances = getProcessInstances(query); return convertWorkflows(instances); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_ACTIVE_WORKFLOW_INSTS, query.getWorkflowDefinitionId()); throw new WorkflowException(msg, e); } } @Override public long countWorkflows(WorkflowInstanceQuery workflowInstanceQuery) { // MNT-9074 My Tasks fails to render if tasks quantity is excessive return getWorkflows(workflowInstanceQuery).size(); } @Override public long countTasks(WorkflowTaskQuery query) { return queryTasks(query).size(); } @SuppressWarnings("unchecked") private List<ProcessInstance> getProcessInstances(final WorkflowInstanceQuery query) { return (List<ProcessInstance>) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { Session session = context.getSession(); StringBuilder processSelect = new StringBuilder(1024) .append("select process from org.jbpm.graph.exe.ProcessInstance as process"); StringBuilder processWhere = new StringBuilder(1024); Map<String, Object> processMap = new TreeMap<String, Object>(); Long processDefId = query.getWorkflowDefinitionId() == null ? null : getJbpmId(query.getWorkflowDefinitionId()); if (processDefId != null) { processSelect.append(" join process.processDefinition as definition"); processWhere.append(" and definition.id = :processDefId"); processMap.put("processDefId", processDefId); } List<String> exludedDefs = query.getExcludedDefinitions(); if (exludedDefs != null && exludedDefs.size() > 0) { if (processDefId == null) { processSelect.append(" join process.processDefinition as definition"); } int exDefNum = 0; for (String exDef : exludedDefs) { exDefNum++; String varExDef = "varExDef" + exDefNum; exDef = BPMEngineRegistry.getLocalId(exDef); exDef = exDef.replaceAll("\\*", "%"); processWhere.append(" and definition.name not like :").append(varExDef); processMap.put(varExDef, exDef); } } if (Boolean.TRUE.equals(query.getActive())) { processWhere.append(" and process.end is null"); } else if (Boolean.FALSE.equals(query.getActive())) { processWhere.append(" and process.end is not null"); } // Check start range if (query.getStartBefore() != null) { processWhere.append(" and process.start <= :processStartBefore"); processMap.put("processStartBefore", query.getStartBefore()); } if (query.getStartAfter() != null) { processWhere.append(" and process.start >= :processStartAfter"); processMap.put("processStartAfter", query.getStartAfter()); } // check end range if (query.getEndBefore() != null) { processWhere.append(" and process.end <= :processEndBefore"); processMap.put("processEndBefore", query.getEndBefore()); } if (query.getEndAfter() != null) { processWhere.append(" and process.end >= :processEndAfter"); processMap.put("processEndAfter", query.getEndAfter()); } if (query.getCustomProps() != null) { processSelect.append(" join process.instances as contextInstance") .append(" join contextInstance.tokenVariableMaps as tokenVariableMap"); processWhere.append(" and contextInstance.class = org.jbpm.context.exe.ContextInstance") .append(" and tokenVariableMap.token = process.rootToken"); int i = 0; for (Map.Entry<QName, Object> prop : query.getCustomProps().entrySet()) { i++; String variable = "var" + i; processMap.put(variable + "name", factory.mapQNameToName(prop.getKey())); if (prop.getValue() == null) { processSelect.append(", ").append(NullInstance.class.getName()).append(" as ") .append(variable); processWhere.append(" and ").append(variable) .append(".tokenVariableMap = tokenVariableMap").append(" and ").append(variable) .append(".name = :").append(variable).append("name"); } else { PropertyDefinition propertyDefinition = dictionaryService.getProperty(prop.getKey()); if (propertyDefinition == null) { processSelect.append(", ").append(StringInstance.class.getName()).append(" as ") .append(variable); processWhere.append(" and ").append(variable) .append(".tokenVariableMap = tokenVariableMap").append(" and ") .append(variable).append(".name = :").append(variable).append("name") .append(" and ").append(variable).append(".value = :").append(variable) .append("value"); processMap.put(variable + "value", prop.getValue().toString()); } else { String propertyType = propertyDefinition.getDataType().getJavaClassName(); if (propertyType.equals("java.lang.String")) { processSelect.append(", ").append(StringInstance.class.getName()).append(" as ") .append(variable); processWhere.append(" and ").append(variable) .append(".tokenVariableMap = tokenVariableMap").append(" and ") .append(variable).append(".name = :").append(variable).append("name") .append(" and ").append(variable).append(".value = :").append(variable) .append("value"); processMap.put(variable + "value", prop.getValue().toString()); } else if (propertyType.equals("java.lang.Long") || propertyType.equals("java.lang.Integer")) { processSelect.append(", ").append(LongInstance.class.getName()).append(" as ") .append(variable); processWhere.append(" and ").append(variable) .append(".tokenVariableMap = tokenVariableMap").append(" and ") .append(variable).append(".name = :").append(variable).append("name") .append(" and ").append(variable).append(".value = :").append(variable) .append("value"); processMap.put(variable + "value", new Long(prop.getValue().toString())); } else if (propertyType.equals("java.util.Date")) { processSelect.append(", ").append(DateInstance.class.getName()).append(" as ") .append(variable); Map<DatePosition, Date> dateProps = (Map<DatePosition, Date>) prop.getValue(); int datePropNum = 0; for (Map.Entry<DatePosition, Date> dateProp : dateProps.entrySet()) { datePropNum++; if (dateProp.getValue() != null) { processWhere.append(" and ").append(variable) .append(".tokenVariableMap = tokenVariableMap").append(" and ") .append(variable).append(".name = :").append(variable) .append("name"); if (dateProp.getKey() == DatePosition.BEFORE) { processMap.put(variable + "value" + datePropNum, calculateBeforeMidnight(dateProp.getValue())); processWhere.append(" and ").append(variable).append(".value <= :") .append(variable).append("value").append(datePropNum); } if (dateProp.getKey() == DatePosition.AFTER) { processMap.put(variable + "value" + datePropNum, calculateMidnight(dateProp.getValue())); processWhere.append(" and ").append(variable).append(".value >= :") .append(variable).append("value").append(datePropNum); } } } } } } } } if (processWhere.length() > 4) { processSelect.append(" where"); processSelect.append(processWhere, 4, processWhere.length()); } processSelect.append(" order by process.id"); return session.createQuery(processSelect.toString()).setProperties(processMap).list(); } }); } private Date calculateBeforeMidnight(Date date) { Date calc = DateUtils.truncate(date, Calendar.DATE); calc = DateUtils.addDays(calc, 1); return DateUtils.addSeconds(calc, -1); } private Date calculateMidnight(Date date) { return DateUtils.truncate(date, Calendar.DATE); } /** * {@inheritDoc} */ public WorkflowInstance getWorkflowById(final String workflowId) { try { return (WorkflowInstance) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { // retrieve workflow GraphSession graphSession = context.getGraphSession(); ProcessInstance processInstance = getProcessInstanceIfExists(graphSession, workflowId); try { return createWorkflowInstance(processInstance); } catch (Exception ex) { logger.warn("Unable to load workflow instance: '" + processInstance + "' due to exception.", ex); return null; } } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_WORKFLOW_INST_BY_ID); throw new WorkflowException(msg, e); } } private ProcessInstance getProcessInstanceIfExists(GraphSession graphSession, String workflowId) { ProcessInstance processInstance = graphSession.getProcessInstance(getJbpmId(workflowId)); if ((processInstance != null) && (tenantService.isEnabled())) { try { tenantService.checkDomain(processInstance.getProcessDefinition().getName()); // throws // exception // if // domain // mismatch } catch (RuntimeException re) { processInstance = null; } } return processInstance; } /** * Gets a jBPM Process Instance * * @param graphSession * jBPM graph session * @param workflowId * workflow id * @return process instance */ protected ProcessInstance getProcessInstance(GraphSession graphSession, String workflowId) { ProcessInstance processInstance = getProcessInstanceIfExists(graphSession, workflowId); if (processInstance == null) { String msg = messageService.getMessage(ERR_GET_PROCESS_INSTANCE, workflowId); throw new WorkflowException(msg); } return processInstance; } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowComponent#getWorkflowPaths(java.lang * .String) */ @SuppressWarnings("unchecked") public List<WorkflowPath> getWorkflowPaths(final String workflowId) { try { return (List<WorkflowPath>) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { // retrieve process instance GraphSession graphSession = context.getGraphSession(); ProcessInstance processInstance = getProcessInstance(graphSession, workflowId); // convert jBPM tokens to workflow posisitons List<Token> tokens = processInstance.findAllTokens(); List<WorkflowPath> paths = new ArrayList<WorkflowPath>(tokens.size()); for (Token token : tokens) { if (!token.hasEnded()) { WorkflowPath path = createWorkflowPath(token); paths.add(path); } } return paths; } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_WORKFLOW_PATHS, workflowId); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowComponent#getPathProperties(java.lang * .String) */ @SuppressWarnings("unchecked") public Map<QName, Serializable> getPathProperties(final String pathId) { try { return (Map<QName, Serializable>) jbpmTemplate.execute(new JbpmCallback() { public Map<QName, Serializable> doInJbpm(JbpmContext context) { // retrieve jBPM token for workflow position GraphSession graphSession = context.getGraphSession(); Token token = getWorkflowToken(graphSession, pathId); ContextInstance instanceContext = token.getProcessInstance().getContextInstance(); Map<QName, Serializable> properties = new HashMap<QName, Serializable>(10); while (token != null) { TokenVariableMap varMap = instanceContext.getTokenVariableMap(token); if (varMap != null) { Map<String, Object> tokenVars = varMap.getVariablesLocally(); for (Map.Entry<String, Object> entry : tokenVars.entrySet()) { String key = entry.getKey(); QName qname = factory.mapNameToQName(key); if (!properties.containsKey(qname)) { Serializable value = convertValue(entry.getValue()); properties.put(qname, value); } } } token = token.getParent(); } return properties; } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_PATH_PROPERTIES, pathId); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowComponent#cancelWorkflow(java.lang * .String) */ public WorkflowInstance cancelWorkflow(final String workflowId) { return cancelWorkflows(Collections.singletonList(workflowId)).get(0); } /* (non-Javadoc) * @see org.alfresco.repo.workflow.WorkflowComponent#cancelWorkflows */ @SuppressWarnings("unchecked") public List<WorkflowInstance> cancelWorkflows(final List<String> workflowIds) { return (List<WorkflowInstance>) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { // Bypass the cache making sure not to flush it Session session = context.getSession(); CacheMode cacheMode = session.getCacheMode(); FlushMode flushMode = session.getFlushMode(); session.setCacheMode(CacheMode.GET); session.setFlushMode(FlushMode.MANUAL); try { List<WorkflowInstance> workflowInstances = new ArrayList<WorkflowInstance>(workflowIds.size()); Map<String, ProcessInstance> processInstances = new HashMap<String, ProcessInstance>( workflowIds.size() * 2); GraphSession graphSession = context.getGraphSession(); // retrieve and cancel process instances for (String workflowId : workflowIds) { try { ProcessInstance processInstance = getProcessInstance(graphSession, workflowId); processInstance.getContextInstance().setVariable("cancelled", true); processInstance.end(); processInstances.put(workflowId, processInstance); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_CANCEL_WORKFLOW, workflowId); throw new WorkflowException(msg, JbpmAccessor.convertJbpmException(e)); } } // Flush at the end of the batch session.flush(); for (String workflowId : workflowIds) { try { // retrieve process instance ProcessInstance processInstance = processInstances.get(workflowId); // TODO: Determine if this is the most appropriate way to cancel workflow... // It might be useful to record point at which it was cancelled etc try { workflowInstances.add(createWorkflowInstance(processInstance)); } catch (Exception ex) { logger.warn("Unable to load workflow instance: '" + processInstance + "' due to exception.", ex); } // delete the process instance graphSession.deleteProcessInstance(processInstance, true, true); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_CANCEL_WORKFLOW, workflowId); throw new WorkflowException(msg, JbpmAccessor.convertJbpmException(e)); } } // Flush at the end of the batch session.flush(); return workflowInstances; } finally { session.setCacheMode(cacheMode); session.setFlushMode(flushMode); } } }); } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowComponent#cancelWorkflow(java.lang * .String) */ public WorkflowInstance deleteWorkflow(final String workflowId) { try { return (WorkflowInstance) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { // retrieve and cancel process instance GraphSession graphSession = context.getGraphSession(); ProcessInstance processInstance = getProcessInstance(graphSession, workflowId); // delete the process instance graphSession.deleteProcessInstance(processInstance, true, true); Date endDate = new Date(); WorkflowInstance workflowInstance = createWorkflowInstance(processInstance, endDate); return workflowInstance; } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_DELETE_WORKFLOW, workflowId); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowComponent#signal(java.lang.String, * java.lang.String) */ public WorkflowPath signal(final String pathId, final String transition) { try { return (WorkflowPath) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { // retrieve jBPM token for workflow position GraphSession graphSession = context.getGraphSession(); Token token = getWorkflowToken(graphSession, pathId); // signal the transition if (transition == null) { token.signal(); } else { Node node = token.getNode(); if (!node.hasLeavingTransition(transition)) { throw new WorkflowException("Transition '" + transition + "' is invalid for Workflow path '" + pathId + "'"); } token.signal(transition); } // save ProcessInstance processInstance = token.getProcessInstance(); context.save(processInstance); // return new workflow path return createWorkflowPath(token); } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_SIGNAL_TRANSITION, transition, pathId); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowComponent#fireEvent(java.lang.String, * java.lang.String) */ public WorkflowPath fireEvent(final String pathId, final String event) { try { return (WorkflowPath) jbpmTemplate.execute(new JbpmCallback() { @SuppressWarnings("unchecked") public Object doInJbpm(JbpmContext context) { // NOTE: Do not allow jBPM built-in events to be fired if (event.equals(Event.EVENTTYPE_AFTER_SIGNAL) || event.equals(Event.EVENTTYPE_BEFORE_SIGNAL) || event.equals(Event.EVENTTYPE_NODE_ENTER) || event.equals(Event.EVENTTYPE_NODE_LEAVE) || event.equals(Event.EVENTTYPE_PROCESS_END) || event.equals(Event.EVENTTYPE_PROCESS_START) || event.equals(Event.EVENTTYPE_SUBPROCESS_CREATED) || event.equals(Event.EVENTTYPE_SUBPROCESS_END) || event.equals(Event.EVENTTYPE_SUPERSTATE_ENTER) || event.equals(Event.EVENTTYPE_SUPERSTATE_LEAVE) || event.equals(Event.EVENTTYPE_TASK_ASSIGN) || event.equals(Event.EVENTTYPE_TASK_CREATE) || event.equals(Event.EVENTTYPE_TASK_END) || event.equals(Event.EVENTTYPE_TASK_START) || event.equals(Event.EVENTTYPE_TIMER) || event.equals(Event.EVENTTYPE_TRANSITION)) { String msg = messageService.getMessage(ERR_INVALID_EVENT, event); throw new WorkflowException(msg); } // retrieve jBPM token for workflow position GraphSession graphSession = context.getGraphSession(); Token token = getWorkflowToken(graphSession, pathId); ExecutionContext executionContext = new ExecutionContext(token); TaskMgmtSession taskSession = context.getTaskMgmtSession(); List<TaskInstance> tasks = taskSession.findTaskInstancesByToken(token.getId()); if (tasks.size() == 0) { // fire the event against current node for the token Node node = token.getNode(); node.fireEvent(event, executionContext); } else { // fire the event against tasks associated with the node // NOTE: this will also propagate the event to the node for (TaskInstance task : tasks) { executionContext.setTaskInstance(task); task.getTask().fireEvent(event, executionContext); } } // save ProcessInstance processInstance = token.getProcessInstance(); context.save(processInstance); // return new workflow path return createWorkflowPath(token); } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_FIRE_EVENT, event, pathId); throw new WorkflowException(msg, e); } } /** * {@inheritDoc} */ public List<WorkflowTask> getTasksForWorkflowPath(String pathId) { return getTasksForWorkflowPath(pathId, false); } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowComponent#getTasksForWorkflowPath( * java.lang.String) */ @SuppressWarnings("unchecked") public List<WorkflowTask> getTasksForWorkflowPath(final String pathId, final boolean sameSession) { try { return (List<WorkflowTask>) jbpmTemplate.execute(new JbpmCallback() { public List<WorkflowTask> doInJbpm(JbpmContext context) { // retrieve tasks at specified workflow path GraphSession graphSession = context.getGraphSession(); Token token = getWorkflowToken(graphSession, pathId); TaskMgmtSession taskSession = context.getTaskMgmtSession(); List<TaskInstance> tasks = taskSession.findTaskInstancesByToken(token.getId()); return getWorkflowTasks(tasks, sameSession); } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_TASKS_FOR_PATH, pathId); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.WorkflowComponent#getTimers(java.lang.String) */ @SuppressWarnings("unchecked") public List<WorkflowTimer> getTimers(final String workflowId) { try { return (List<WorkflowTimer>) jbpmTemplate.execute(new JbpmCallback() { public List<WorkflowTimer> doInJbpm(JbpmContext context) { // retrieve process GraphSession graphSession = context.getGraphSession(); ProcessInstance process = getProcessInstance(graphSession, workflowId); // retrieve timers for process Session session = context.getSession(); Query query = session.createQuery(PROCESS_TIMERS_QUERY); query.setEntity("process", process); List<Timer> timers = query.list(); // convert timers to appropriate service response format List<WorkflowTimer> workflowTimers = new ArrayList<WorkflowTimer>(timers.size()); for (Timer timer : timers) { WorkflowTimer workflowTimer = createWorkflowTimer(timer); workflowTimers.add(workflowTimer); } return workflowTimers; } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_TIMERS, workflowId); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see org.alfresco.repo.workflow.WorkflowComponent#hasWorkflowImage(java.lang.String) */ public boolean hasWorkflowImage(final String workflowInstanceId) { // we don't support workflow instance diagrams in JBPM so return false return false; } /* * (non-Javadoc) * * @see org.alfresco.repo.workflow.WorkflowComponent#getWorkflowImage(java.lang.String) */ public InputStream getWorkflowImage(final String workflowInstanceId) { // we don't support workflow instance diagrams in JBPM so return null return null; } // // Task Management ... // /** * {@inheritDoc} */ public List<WorkflowTask> getAssignedTasks(String authority, WorkflowTaskState state, boolean lazyInitialization) { return getAssignedTasksInternal(authority, state, false); } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.TaskComponent#getAssignedTasks(java.lang.String * , org.alfresco.service.cmr.workflow.WorkflowTaskState) */ @SuppressWarnings("unchecked") public List<WorkflowTask> getAssignedTasksInternal(final String authority, final WorkflowTaskState state, final boolean sameSession) { try { return (List<WorkflowTask>) jbpmTemplate.execute(new JbpmCallback() { public List<WorkflowTask> doInJbpm(JbpmContext context) { // retrieve tasks assigned to authority List<TaskInstance> tasks; if (state.equals(WorkflowTaskState.IN_PROGRESS)) { return findActiveTaskInstances(authority, context); } else { // Note: This method is not implemented by jBPM tasks = findCompletedTaskInstances(context, authority); return getWorkflowTasks(tasks, sameSession); } } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_ASSIGNED_TASKS, authority, state); throw new WorkflowException(msg, e); } } /** * 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) { try { WorkflowTask workflowTask = makeWorkflowTask(row, taskInstanceCache, variablesCache); if (workflowTask != null) { workflowTasks.add(workflowTask); } } catch (Exception ex) { logger.warn("Unable to load workflow instance: '" + row[0] + "' due to exception.", ex); continue; } } 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.getTokenVariableMap(token).getToken().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.getToken().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, final boolean lazyInitialization) { try { return (List<WorkflowTask>) jbpmTemplate.execute(new JbpmCallback() { public List<WorkflowTask> doInJbpm(JbpmContext context) { // retrieve pooled tasks for all flattened authorities TaskMgmtSession taskSession = context.getTaskMgmtSession(); List<TaskInstance> tasks = taskSession.findPooledTaskInstances(authorities); return getWorkflowTasks(tasks); } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_POOLED_TASKS, authorities); throw new WorkflowException(msg, e); } } /* (non-Javadoc) * @see org.alfresco.repo.workflow.TaskComponent#queryTasks(org.alfresco.service.cmr.workflow.WorkflowTaskQuery) */ @Override public List<WorkflowTask> queryTasks(WorkflowTaskQuery query) { return queryTasks(query, false); } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.TaskComponent#queryTasks(org.alfresco.service * .cmr.workflow.WorkflowTaskFilter, boolean) */ @SuppressWarnings("unchecked") public List<WorkflowTask> queryTasks(final WorkflowTaskQuery query, final boolean sameSession) { try { return (List<WorkflowTask>) jbpmTemplate.execute(new JbpmCallback() { public List<WorkflowTask> doInJbpm(JbpmContext context) { // Bypass the cache making sure not to flush it Session session = context.getSession(); CacheMode cacheMode = session.getCacheMode(); try { session.setCacheMode(CacheMode.GET); Criteria criteria = createTaskQueryCriteria(session, query); List<TaskInstance> tasks = criteria.list(); return getWorkflowTasks(tasks, sameSession); } finally { session.setCacheMode(cacheMode); } } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_QUERY_TASKS, query); throw new WorkflowException(msg, e); } } protected List<WorkflowTask> getWorkflowTasks(List<TaskInstance> tasks) { return getWorkflowTasks(tasks, false); } protected List<WorkflowTask> getWorkflowTasks(List<TaskInstance> tasks, boolean sameSession) { final List<TaskInstance> filteredTasks; if (tenantService.isEnabled()) { filteredTasks = new ArrayList<TaskInstance>(tasks.size()); for (TaskInstance task : tasks) { try { tenantService.checkDomain(task.getTask().getProcessDefinition().getName()); filteredTasks.add(task); } catch (RuntimeException re) { // deliberately skip this one - due to domain mismatch - eg. // when querying by group authority continue; } } } else { filteredTasks = tasks; } List<WorkflowTask> workflowTasks; if (sameSession) { workflowTasks = new AbstractList<WorkflowTask>() { @Override public WorkflowTask get(int index) { TaskInstance task = filteredTasks.get(index); return createWorkflowTask(task); } @Override public int size() { return filteredTasks.size(); } }; } else { workflowTasks = new ArrayList<WorkflowTask>(filteredTasks.size()); for (TaskInstance task : filteredTasks) { try { WorkflowTask workflowTask = createWorkflowTask(task); workflowTasks.add(workflowTask); } catch (Exception ex) { logger.warn("Unable to load workflow task: '" + task + "' due to exception.", ex); continue; } } } return workflowTasks; } /** * Construct a JBPM Hibernate query based on the Task Query provided * * @param session Session * @param query WorkflowTaskQuery * @return jbpm hiberate query criteria */ private Criteria createTaskQueryCriteria(Session session, WorkflowTaskQuery query) { 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", factory.mapQNameToName(prop.getKey()))); value.add(Restrictions.eq("value", prop.getValue().toString())); values.add(value); } variables.add(values); } } // process criteria Criteria process = createProcessCriteria(task, query); // 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(Projections.distinct(Property.forName("processInstance"))); Disjunction values = Restrictions.disjunction(); for (Map.Entry<QName, Object> prop : props.entrySet()) { Conjunction value = Restrictions.conjunction(); value.add(Restrictions.eq("name", factory.mapQNameToName(prop.getKey()))); value.add(Restrictions.eq("value", prop.getValue().toString())); values.add(value); } variables.add(values); // note: constrain process variables to same criteria as tasks createProcessCriteria(variables, query); Disjunction processIdCriteria = createProcessIdCriteria(variables); // constrain tasks by process list process = (process == null) ? task.createCriteria("processInstance") : process; process.add(processIdCriteria); } } // 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.TaskDue_Asc) { task.addOrder(Order.asc("dueDate")); } else if (orderByPart == WorkflowTaskQuery.OrderBy.TaskDue_Desc) { task.addOrder(Order.desc("dueDate")); } 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")); } } } // limit results if (query.getLimit() != -1) { task.setMaxResults(query.getLimit()); } return task; } /** * @param variables Criteria * @return Disjunction */ private Disjunction createProcessIdCriteria(Criteria variables) { // retrieve list of processes matching specified variables List<?> processList = variables.list(); Object[] processIds = getProcessIds(processList); // ALF-5841 fix int batch = 0; List<Object> buf = new ArrayList<Object>(1000); Disjunction ids = Restrictions.disjunction(); for (Object id : processIds) { if (batch < 1000) { batch++; buf.add(id); } else { ids.add(Restrictions.in("id", buf)); batch = 0; buf.clear(); } } if (!buf.isEmpty()) { ids.add(Restrictions.in("id", buf)); } return ids; } private Object[] getProcessIds(List<?> processList) { ArrayList<Object> ids = new ArrayList<Object>(processList.size()); if (processList.isEmpty()) { ids.add(new Long(-1)); } else { for (Object obj : processList) { ProcessInstance instance = (ProcessInstance) obj; ids.add(instance.getId()); } } return ids.toArray(); } /** * Create process-specific query criteria * * @param root Criteria * @param query WorkflowTaskQuery * @return Criteria */ private Criteria createProcessCriteria(Criteria root, WorkflowTaskQuery query) { Criteria process = null; // process active? if (query.isActive() != null) { process = root.createCriteria("processInstance"); if (query.isActive()) { process.add(Restrictions.isNull("end")); } else { process.add(Restrictions.isNotNull("end")); } } // process id if (query.getProcessId() != null) { process = (process == null) ? root.createCriteria("processInstance") : process; process.add(Restrictions.eq("id", getJbpmId(query.getProcessId()))); } // process definition name String definitionName = query.getWorkflowDefinitionName(); if (definitionName != null) { definitionName = createLocalId(definitionName); } if (definitionName == null) { QName qName = query.getProcessName(); definitionName = qName == null ? null : qName.toPrefixString(namespaceService); } if (definitionName != null) { process = (process == null) ? root.createCriteria("processInstance") : process; Criteria processDef = process.createCriteria("processDefinition"); String processName = tenantService.getName(definitionName); processDef.add(Restrictions.eq("name", processName)); } return process; } /** * Gets a jBPM Task Instance * * @param taskSession * jBPM task session * @param taskId * task id * @return task instance */ protected TaskInstance getTaskInstance(TaskMgmtSession taskSession, String taskId) { TaskInstance taskInstance = taskSession.getTaskInstance(getJbpmId(taskId)); if ((taskInstance != null) && (tenantService.isEnabled())) { try { // throws exception if domain mismatch tenantService.checkDomain(taskInstance.getTask().getProcessDefinition().getName()); } catch (RuntimeException re) { taskInstance = null; } } if (taskInstance == null) { String msg = messageService.getMessage(ERR_GET_TASK_INST, taskId); throw new WorkflowException(msg); } return taskInstance; } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.TaskComponent#updateTask(java.lang.String, * java.util.Map, java.util.Map, java.util.Map) */ public WorkflowTask updateTask(final String taskId, final Map<QName, Serializable> properties, final Map<QName, List<NodeRef>> add, final Map<QName, List<NodeRef>> remove) { try { return (WorkflowTask) jbpmTemplate.execute(new JbpmCallback() { @SuppressWarnings("unchecked") public Object doInJbpm(JbpmContext context) { // retrieve task TaskMgmtSession taskSession = context.getTaskMgmtSession(); TaskInstance taskInstance = getTaskInstance(taskSession, taskId); // create properties to set on task instance Map<QName, Serializable> newProperties = new HashMap<QName, Serializable>(10); if (properties != null) { newProperties.putAll(properties); } Map<QName, Serializable> existingProperties = getTaskProperties(taskInstance, false); if (add != null) { // add new associations for (Entry<QName, List<NodeRef>> toAdd : add.entrySet()) { // retrieve existing list of noderefs for // association QName key = toAdd.getKey(); Serializable existingValue = newProperties.get(key); if (existingValue == null) { existingValue = existingProperties.get(key); } // make the additions if (existingValue == null) { newProperties.put(key, (Serializable) toAdd.getValue()); } else { List<NodeRef> existingAdd; if (existingValue instanceof List<?>) { existingAdd = (List<NodeRef>) existingValue; } else { existingAdd = new LinkedList<NodeRef>(); existingAdd.add((NodeRef) existingValue); } for (NodeRef nodeRef : toAdd.getValue()) { if (!(existingAdd.contains(nodeRef))) { existingAdd.add(nodeRef); } } newProperties.put(key, (Serializable) existingAdd); } } } if (remove != null) { // add new associations for (Entry<QName, List<NodeRef>> toRemove : remove.entrySet()) { // retrieve existing list of noderefs for // association QName key = toRemove.getKey(); Serializable existingValue = newProperties.get(key); if (existingValue == null) { existingValue = existingProperties.get(key); } // make the subtractions if (existingValue != null) { if (existingValue instanceof List<?>) { List<NodeRef> existingRemove = (List<NodeRef>) existingValue; existingRemove.removeAll(toRemove.getValue()); newProperties.put(key, (Serializable) existingRemove); } else if (toRemove.getValue().contains(existingValue)) { newProperties.put(key, new LinkedList<NodeRef>()); } } } } // update the task if (newProperties.isEmpty() == false) { setTaskProperties(taskInstance, newProperties); // save ProcessInstance processInstance = taskInstance.getToken().getProcessInstance(); context.save(processInstance); } // note: the ending of a task may not have signalled (i.e. // more than one task exists at // this node) return createWorkflowTask(taskInstance); } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_UPDATE_TASK, taskId); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see org.alfresco.repo.workflow.TaskComponent#startTask(java.lang.String) */ public WorkflowTask startTask(String taskId) { // TODO: throw new UnsupportedOperationException(); } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.TaskComponent#suspendTask(java.lang.String) */ public WorkflowTask suspendTask(String taskId) { // TODO: throw new UnsupportedOperationException(); } /* * (non-Javadoc) * * @see org.alfresco.repo.workflow.TaskComponent#endTask(java.lang.String, * java.lang.String) */ public WorkflowTask endTask(final String taskId, final String transition) { try { return (WorkflowTask) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { // retrieve task TaskMgmtSession taskSession = context.getTaskMgmtSession(); TaskInstance taskInstance = getTaskInstance(taskSession, taskId); // ensure all mandatory properties have been provided QName[] missingProps = getMissingMandatoryTaskProperties(taskInstance); if (missingProps != null && missingProps.length > 0) { String props = ""; for (int i = 0; i < missingProps.length; i++) { props += missingProps[i].toString() + ((i < missingProps.length - 1) ? "," : ""); } String msg = messageService.getMessage(ERR_MANDATORY_TASK_PROPERTIES_MISSING, props); throw new WorkflowException(msg); } // signal the transition on the task if (transition == null) { taskInstance.end(); } else { Node node = taskInstance.getToken().getNode(); if (node.getLeavingTransition(transition) == null) { String msg = messageService.getMessage(ERR_END_TASK_INVALID_TRANSITION, transition, taskId); throw new WorkflowException(msg); } taskInstance.end(transition); } // save ProcessInstance processInstance = taskInstance.getToken().getProcessInstance(); context.save(processInstance); // note: the ending of a task may not have signalled (i.e. // more than one task exists at // this node) return createWorkflowTask(taskInstance); } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_END_TASK, transition, taskId); throw new WorkflowException(msg, e); } } /* * (non-Javadoc) * * @see * org.alfresco.repo.workflow.TaskComponent#getTaskById(java.lang.String) */ public WorkflowTask getTaskById(final String taskId) { try { return (WorkflowTask) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { // retrieve task TaskMgmtSession taskSession = context.getTaskMgmtSession(); TaskInstance taskInstance = getTaskInstance(taskSession, taskId); return createWorkflowTask(taskInstance); } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_TASK_BY_ID, taskId); throw new WorkflowException(msg, e); } } @Override public WorkflowTask getStartTask(final String workflowInstanceId) { try { return (WorkflowTask) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { // retrieve process instance GraphSession graphSession = context.getGraphSession(); ProcessInstance processInstance = getProcessInstanceIfExists(graphSession, workflowInstanceId); if (processInstance == null) { return null; } Task startTask = processInstance.getProcessDefinition().getTaskMgmtDefinition().getStartTask(); if (startTask == null) { return null; } // retrieve task Session session = context.getSession(); Criteria taskCriteria = session.createCriteria(TaskInstance.class); taskCriteria.add(Restrictions.eq("name", startTask.getName())); Criteria process = taskCriteria.createCriteria("processInstance"); process.add(Restrictions.eq("id", processInstance.getId())); TaskInstance taskInstance = (TaskInstance) taskCriteria.uniqueResult(); if (taskInstance == null) { return null; } return createWorkflowTask(taskInstance); } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_GET_START_TASK, workflowInstanceId); throw new WorkflowException(msg, e); } } @SuppressWarnings("unchecked") public List<WorkflowTask> getStartTasks(final List<String> workflowInstanceIds, final boolean sameSession) { try { return (List<WorkflowTask>) jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { List<Long> jbpmIds = new ArrayList<Long>(workflowInstanceIds.size()); Set<String> startTaskNames = new TreeSet<String>(); for (String workflowInstanceId : workflowInstanceIds) { // retrieve process instance GraphSession graphSession = context.getGraphSession(); ProcessInstance processInstance = getProcessInstanceIfExists(graphSession, workflowInstanceId); if (processInstance != null) { jbpmIds.add(processInstance.getId()); Task startTask = processInstance.getProcessDefinition().getTaskMgmtDefinition() .getStartTask(); startTaskNames.add(startTask.getName()); } } if (jbpmIds.isEmpty()) { return Collections.emptyList(); } // retrieve tasks Session session = context.getSession(); Criteria taskCriteria = session.createCriteria(TaskInstance.class); taskCriteria.add(Restrictions.in("name", startTaskNames)); Criteria process = taskCriteria.createCriteria("processInstance"); process.add(Restrictions.in("id", jbpmIds)); // Bypass the cache making sure not to flush it CacheMode cacheMode = session.getCacheMode(); try { session.setCacheMode(CacheMode.GET); List<TaskInstance> tasks = process.list(); return getWorkflowTasks(tasks, sameSession); } finally { session.setCacheMode(cacheMode); } } }); } catch (JbpmException e) { String msg = messageService.getMessage(ERR_QUERY_TASKS, workflowInstanceIds); throw new WorkflowException(msg, e); } } // // Helpers... // /** * Process Definition with accompanying problems */ private static class CompiledProcessDefinition { public CompiledProcessDefinition(ProcessDefinition def, List<Problem> problems) { this.def = def; this.problems = new String[problems.size()]; int i = 0; for (Problem problem : problems) { this.problems[i++] = problem.toString(); } } protected ProcessDefinition def; protected String[] problems; } /** * Construct a Process Definition from the provided Process Definition * stream * * @param definitionStream * stream to create process definition from * @param mimetype * mimetype of stream * @return process definition */ @SuppressWarnings("unchecked") protected CompiledProcessDefinition compileProcessDefinition(InputStream definitionStream, String mimetype) { String actualMimetype = (mimetype == null) ? MimetypeMap.MIMETYPE_ZIP : mimetype; CompiledProcessDefinition compiledDef = null; // parse process definition from jBPM process archive file if (actualMimetype.equals(MimetypeMap.MIMETYPE_ZIP)) { ZipInputStream zipInputStream = null; try { zipInputStream = new ZipInputStream(definitionStream); ProcessArchive reader = new ProcessArchive(zipInputStream); ProcessDefinition def = reader.parseProcessDefinition(); compiledDef = new CompiledProcessDefinition(def, reader.getProblems()); } catch (Exception e) { String msg = messageService.getMessage(ERR_COMPILE_PROCESS_DEF_zip); throw new JbpmException(msg, e); } finally { if (zipInputStream != null) { try { zipInputStream.close(); } catch (IOException e) { // Intentionally empty! } } } } // parse process definition from jBPM xml file else if (actualMimetype.equals(MimetypeMap.MIMETYPE_XML)) { try { JBPMJpdlXmlReader jpdlReader = new JBPMJpdlXmlReader(definitionStream); ProcessDefinition def = jpdlReader.readProcessDefinition(); List<Problem> problems = jpdlReader.getProblems(); compiledDef = new CompiledProcessDefinition(def, problems); } catch (Exception e) { String msg = messageService.getMessage(ERR_COMPILE_PROCESS_DEF_XML); throw new JbpmException(msg, e); } } else { String msg = messageService.getMessage(ERR_COMPILE_PROCESS_DEF_UNSUPPORTED, mimetype); throw new JbpmException(msg); } if (tenantService.isEnabled()) { compiledDef.def.setName(tenantService.getName(compiledDef.def.getName())); } return compiledDef; } /** * Get JBoss JBPM Id from Engine Global Id * * @param id * global id * @return JBoss JBPM Id */ protected long getJbpmId(String id) { try { String theLong = createLocalId(id); return new Long(theLong); } catch (NumberFormatException e) { String msg = messageService.getMessage(ERR_GET_JBPM_ID, id); throw new WorkflowException(msg, e); } } /** * Get the JBoss JBPM Token for the Workflow Path * * @param session * JBoss JBPM Graph Session * @param pathId * workflow path id * @return JBoss JBPM Token */ protected Token getWorkflowToken(GraphSession session, String pathId) { // extract process id and token path within process String[] path = pathId.split(WORKFLOW_PATH_SEPERATOR); if (path.length != 2) { String msg = messageService.getMessage(ERR_GET_WORKFLOW_TOKEN_INVALID, pathId); throw new WorkflowException(msg); } // retrieve jBPM token for workflow position ProcessInstance processInstance = getProcessInstance(session, path[0]); String tokenId = path[1].replace(WORKFLOW_TOKEN_SEPERATOR, "/"); Token token = processInstance.findToken(tokenId); if (token == null) { String msg = messageService.getMessage(ERR_GET_WORKFLOW_TOKEN_NULL, pathId); throw new WorkflowException(msg); } return token; } /** * Gets Properties of Task * * @param instance task instance * @param localProperties 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 localProperties properties to set * @param variablesCache cahce of context instance variables if any exists */ @SuppressWarnings("unchecked") protected Map<QName, Serializable> getTaskProperties(TaskInstance instance, boolean localProperties, Map<Long, TokenVariableMap> variablesCache) { // retrieve type definition for task TypeDefinition taskDef = getFullTaskDefinition(instance); Map<QName, PropertyDefinition> taskProperties = taskDef.getProperties(); Map<QName, AssociationDefinition> taskAssocs = taskDef.getAssociations(); // build properties from jBPM context (visit all tokens to the root) Map<String, Object> vars = instance.getVariablesLocally(); if (!localProperties) { ContextInstance context = instance.getContextInstance(); Token token = instance.getToken(); while (token != null) { TokenVariableMap varMap = null; if (variablesCache != null && variablesCache.containsKey(context.getTokenVariableMap(token).getToken().getId())) { varMap = variablesCache.get(context.getTokenVariableMap(token).getToken().getId()); } else { varMap = context.getTokenVariableMap(token); } if (varMap != null) { Map<String, Object> tokenVars = varMap.getVariablesLocally(); for (Map.Entry<String, Object> entry : tokenVars.entrySet()) { if (!vars.containsKey(entry.getKey())) { vars.put(entry.getKey(), entry.getValue()); } } } token = token.getParent(); } } // map arbitrary task variables Map<QName, Serializable> properties = new HashMap<QName, Serializable>(10); for (Entry<String, Object> entry : vars.entrySet()) { String key = entry.getKey(); QName qname = factory.mapNameToQName(key); // add variable, only if part of task definition or locally defined // on task boolean isAssoc = taskAssocs.containsKey(qname); if (taskProperties.containsKey(qname) || isAssoc || instance.hasVariableLocally(key)) { Serializable value = convertValue(taskProperties.get(qname), entry.getValue()); properties.put(qname, value); } } // map jBPM task instance fields to properties properties.put(WorkflowModel.PROP_TASK_ID, instance.getId()); properties.put(WorkflowModel.PROP_DESCRIPTION, instance.getDescription()); properties.put(WorkflowModel.PROP_START_DATE, instance.getStart()); properties.put(WorkflowModel.PROP_DUE_DATE, instance.getDueDate()); properties.put(WorkflowModel.PROP_COMPLETION_DATE, instance.getEnd()); properties.put(WorkflowModel.PROP_PRIORITY, instance.getPriority()); properties.put(ContentModel.PROP_CREATED, instance.getCreate()); properties.put(ContentModel.PROP_OWNER, instance.getActorId()); properties.put(ContentModel.PROP_CREATOR, AuthenticationUtil.getFullyAuthenticatedUser()); // map jBPM comments // NOTE: Only use first comment in list List<Comment> comments = instance.getComments(); if (comments != null && comments.size() > 0) { properties.put(WorkflowModel.PROP_COMMENT, comments.get(0).getMessage()); } // map jBPM task instance collections to associations Set<PooledActor> pooledActors = instance.getPooledActors(); if (pooledActors != null) { List<NodeRef> pooledNodeRefs = new ArrayList<NodeRef>(pooledActors.size()); for (PooledActor pooledActor : pooledActors) { NodeRef pooledNodeRef = null; String pooledActorId = pooledActor.getActorId(); if (AuthorityType.getAuthorityType(pooledActorId) == AuthorityType.GROUP) { pooledNodeRef = mapNameToAuthority(pooledActorId); } else { pooledNodeRef = mapNameToPerson(pooledActorId); } if (pooledNodeRef != null) { pooledNodeRefs.add(pooledNodeRef); } } properties.put(WorkflowModel.ASSOC_POOLED_ACTORS, (Serializable) pooledNodeRefs); } return properties; } private TypeDefinition getFullTaskDefinition(TaskInstance instance) { Task task = instance.getTask(); TypeDefinition taskType = factory.getTaskTypeDefinition(task.getName(), task.getStartState() != null); TypeDefinition taskDef = dictionaryService.getAnonymousType(taskType.getName()); return taskDef; } /** * Sets Properties of Task * * @param instance * task instance * @param properties * properties to set */ protected void setTaskProperties(TaskInstance instance, Map<QName, Serializable> properties) { if (properties == null) { return; } TypeDefinition taskDef = getFullTaskDefinition(instance); Map<QName, PropertyDefinition> taskProperties = taskDef.getProperties(); Map<QName, AssociationDefinition> taskAssocs = taskDef.getAssociations(); // map each parameter to task for (Entry<QName, Serializable> entry : properties.entrySet()) { QName key = entry.getKey(); Serializable value = entry.getValue(); // determine if writing property // NOTE: some properties map to fields on jBPM task instance whilst // others are set in the general variable bag on the task PropertyDefinition propDef = taskProperties.get(key); if (propDef != null) { if (propDef.isProtected()) { // NOTE: only write non-protected properties continue; } // convert property value if (value instanceof Collection<?>) { value = (Serializable) DefaultTypeConverter.INSTANCE.convert(propDef.getDataType(), (Collection<?>) value); } else { value = (Serializable) DefaultTypeConverter.INSTANCE.convert(propDef.getDataType(), value); } // convert NodeRefs to JBPMNodes DataTypeDefinition dataTypeDef = propDef.getDataType(); if (dataTypeDef.getName().equals(DataTypeDefinition.NODE_REF)) { value = convertNodeRefs(propDef.isMultiValued(), value); } // map property to specific jBPM task instance field if (key.equals(WorkflowModel.PROP_DESCRIPTION)) { if (value != null && !(value instanceof String)) { throw getInvalidPropertyValueException(key, value); } instance.setDescription((String) value); continue; } if (key.equals(WorkflowModel.PROP_DUE_DATE)) { if (value != null && !(value instanceof Date)) { throw getInvalidPropertyValueException(key, value); } instance.setDueDate((Date) value); continue; } else if (key.equals(WorkflowModel.PROP_PRIORITY)) { if (!(value instanceof Integer)) { throw getInvalidPropertyValueException(key, value); } instance.setPriority((Integer) value); continue; } else if (key.equals(WorkflowModel.PROP_COMMENT)) { if (!(value instanceof String)) { throw getInvalidPropertyValueException(key, value); } // NOTE: Only use first comment in list final List<?> comments = instance.getComments(); if (comments != null && comments.size() > 0) { // remove existing comments // TODO: jBPM does not provide assistance here jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { Session session = context.getSession(); for (Object obj : comments) { Comment comment = (Comment) obj; comment.getToken().getComments().remove(comment); session.delete(comment); } comments.clear(); return null; } }); } instance.addComment((String) value); continue; } else if (key.equals(ContentModel.PROP_OWNER)) { if (value != null && !(value instanceof String)) { throw getInvalidPropertyValueException(key, value); } String actorId = (String) value; String existingActorId = instance.getActorId(); if (existingActorId == null || !existingActorId.equals(actorId)) { instance.setActorId((String) value, false); } continue; } } else { // determine if writing association AssociationDefinition assocDef = taskAssocs.get(key); if (assocDef != null) { // convert association to JBPMNodes value = convertNodeRefs(assocDef.isTargetMany(), value); // map association to specific jBPM task instance field if (key.equals(WorkflowModel.ASSOC_POOLED_ACTORS)) { String[] pooledActors = null; if (value instanceof JBPMNodeList) { JBPMNodeList actors = (JBPMNodeList) value; pooledActors = new String[actors.size()]; int i = 0; for (JBPMNode actor : actors) { pooledActors[i++] = mapAuthorityToName(actor.getNodeRef()); } } else if (value instanceof JBPMNode) { JBPMNode node = (JBPMNode) value; pooledActors = new String[] { mapAuthorityToName(node.getNodeRef()) }; } else { throw getInvalidPropertyValueException(key, value); } instance.setPooledActors(pooledActors); continue; } } // untyped value, perform minimal conversion else { if (value instanceof NodeRef) { value = new JBPMNode((NodeRef) value, serviceRegistry); } } } // no specific mapping to jBPM task has been established, so place // into // the generic task variable bag String name = factory.mapQNameToName(key); instance.setVariableLocally(name, value); } } private WorkflowException getInvalidPropertyValueException(QName key, Serializable value) { String msg = messageService.getMessage(ERR_SET_TASK_PROPS_INVALID_VALUE, value, key); return new WorkflowException(msg); } /** * Sets Default Properties of Task * * @param instance * task instance */ protected void setDefaultTaskProperties(TaskInstance instance) { Map<QName, Serializable> existingValues = getTaskProperties(instance, false); Map<QName, Serializable> defaultValues = new HashMap<QName, Serializable>(); // construct an anonymous type that flattens all mandatory aspects ClassDefinition classDef = getFullTaskDefinition(instance); Map<QName, PropertyDefinition> propertyDefs = classDef.getProperties(); // for each property, determine if it has a default value for (Map.Entry<QName, PropertyDefinition> entry : propertyDefs.entrySet()) { String defaultValue = entry.getValue().getDefaultValue(); if (defaultValue != null) { if (existingValues.get(entry.getKey()) == null || ignoredProperties.containsValue(entry.getKey())) { defaultValues.put(entry.getKey(), defaultValue); } } } // special case for task description default value String description = (String) existingValues.get(WorkflowModel.PROP_DESCRIPTION); if (description == null || description.length() == 0) { description = (String) instance.getContextInstance() .getVariable(factory.mapQNameToName(WorkflowModel.PROP_WORKFLOW_DESCRIPTION)); if (description != null && description.length() > 0) { defaultValues.put(WorkflowModel.PROP_DESCRIPTION, description); } else { WorkflowTask task = createWorkflowTask(instance); defaultValues.put(WorkflowModel.PROP_DESCRIPTION, task.getTitle()); } } // assign the default values to the task if (defaultValues.size() > 0) { setTaskProperties(instance, defaultValues); } } /** * Sets default description for the Task * * @param instance * task instance */ public void setDefaultStartTaskDescription(TaskInstance instance) { String description = instance.getTask().getDescription(); if (description == null || description.length() == 0) { description = (String) instance.getContextInstance() .getVariable(factory.mapQNameToName(WorkflowModel.PROP_WORKFLOW_DESCRIPTION)); if (description != null && description.length() > 0) { Map<QName, Serializable> defaultValues = new HashMap<QName, Serializable>(); defaultValues.put(WorkflowModel.PROP_DESCRIPTION, description); setTaskProperties(instance, defaultValues); } } } /** * Initialise Workflow Instance properties * * @param startTask * start task instance */ protected void setDefaultWorkflowProperties(TaskInstance startTask) { Map<QName, Serializable> taskProperties = getTaskProperties(startTask, true); ContextInstance processContext = startTask.getContextInstance(); String workflowDescriptionName = factory.mapQNameToName(WorkflowModel.PROP_WORKFLOW_DESCRIPTION); if (!processContext.hasVariable(workflowDescriptionName)) { processContext.setVariable(workflowDescriptionName, taskProperties.get(WorkflowModel.PROP_WORKFLOW_DESCRIPTION)); } 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 = factory.mapQNameToName(WorkflowModel.PROP_WORKFLOW_PRIORITY); if (!processContext.hasVariable(workflowPriorityName)) { processContext.setVariable(workflowPriorityName, taskProperties.get(WorkflowModel.PROP_WORKFLOW_PRIORITY)); } 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 = factory.mapQNameToName(WorkflowModel.PROP_CONTEXT); if (!processContext.hasVariable(workflowContextName)) { Serializable contextRef = taskProperties.get(WorkflowModel.PROP_CONTEXT); processContext.setVariable(workflowContextName, convertNodeRefs(contextRef instanceof List<?>, contextRef)); } } /** * Get missing mandatory properties on Task * * @param instance * task instance * @return array of missing property names (or null, if none) */ protected QName[] getMissingMandatoryTaskProperties(TaskInstance instance) { List<QName> missingProps = null; // retrieve properties of task Map<QName, Serializable> existingValues = getTaskProperties(instance, false); // retrieve definition of task ClassDefinition classDef = getFullTaskDefinition(instance); Map<QName, PropertyDefinition> propertyDefs = classDef.getProperties(); Map<QName, AssociationDefinition> assocDefs = classDef.getAssociations(); // for each property, determine if it is mandatory for (Map.Entry<QName, PropertyDefinition> entry : propertyDefs.entrySet()) { QName name = entry.getKey(); if (!(name.getNamespaceURI().equals(NamespaceService.CONTENT_MODEL_1_0_URI) || (name.getNamespaceURI().equals(NamespaceService.SYSTEM_MODEL_1_0_URI)))) { boolean isMandatory = entry.getValue().isMandatory(); if (isMandatory) { Object value = existingValues.get(entry.getKey()); if (value == null || (value instanceof String && ((String) value).length() == 0)) { if (missingProps == null) { missingProps = new ArrayList<QName>(); } missingProps.add(entry.getKey()); } } } } for (Map.Entry<QName, AssociationDefinition> entry : assocDefs.entrySet()) { QName name = entry.getKey(); if (!(name.getNamespaceURI().equals(NamespaceService.CONTENT_MODEL_1_0_URI) || (name.getNamespaceURI().equals(NamespaceService.SYSTEM_MODEL_1_0_URI)))) { boolean isMandatory = entry.getValue().isTargetMandatory(); if (isMandatory) { Object value = existingValues.get(entry.getKey()); if (value == null || (value instanceof List<?> && ((List<?>) value).isEmpty())) { if (missingProps == null) { missingProps = new ArrayList<QName>(); } missingProps.add(entry.getKey()); } } } } return (missingProps == null) ? null : missingProps.toArray(new QName[missingProps.size()]); } /** * Attempts to convert a JBPM Object to the correct Alfresco data type * @param propDef PropertyDefinition * @param value any Value * @return Serializable */ private Serializable convertValue(PropertyDefinition propDef, Object value) { if (propDef != null && value instanceof String && Boolean.class.getName().equals(propDef.getDataType().getJavaClassName())) { return (Serializable) new BooleanToStringConverter().revert(value); } else { return convertValue(value); } } /** * Convert a jBPM Value to an Alfresco value * * @param value * jBPM value * @return alfresco value */ private Serializable convertValue(Object value) { Serializable alfValue = null; if (value == null) { // NOOP } else if (value instanceof JBPMNode) { alfValue = ((JBPMNode) value).getNodeRef(); } else if (value instanceof JBPMNodeList) { JBPMNodeList nodes = (JBPMNodeList) value; List<NodeRef> nodeRefs = new ArrayList<NodeRef>(nodes.size()); for (JBPMNode node : nodes) { nodeRefs.add(node.getNodeRef()); } alfValue = (Serializable) nodeRefs; } else if (value instanceof Serializable) { alfValue = (Serializable) value; } else { String msg = messageService.getMessage(ERR_CONVERT_VALUE, value); throw new WorkflowException(msg); } return alfValue; } /** * Convert a Repository association to JBPMNodeList or JBPMNode * * @param isMany * true => force conversion to list * @param value * value to convert * @return JBPMNodeList or JBPMNode */ @SuppressWarnings("unchecked") private Serializable convertNodeRefs(boolean isMany, Serializable value) { if (value instanceof NodeRef) { if (isMany) { // convert single node ref to list of node refs JBPMNodeList values = new JBPMNodeList(); values.add(new JBPMNode((NodeRef) value, serviceRegistry)); value = values; } else { value = new JBPMNode((NodeRef) value, serviceRegistry); } } else if (value instanceof List) { if (isMany) { JBPMNodeList values = new JBPMNodeList(); for (NodeRef nodeRef : (List<NodeRef>) value) { values.add(new JBPMNode(nodeRef, serviceRegistry)); } value = values; } else { List<NodeRef> nodeRefs = (List<NodeRef>) value; value = (nodeRefs.size() == 0 ? null : new JBPMNode(nodeRefs.get(0), serviceRegistry)); } } return value; } /** * Convert person name to an Alfresco Person * * @param name * the person name to convert * @return the Alfresco person */ private NodeRef mapNameToPerson(String name) { NodeRef authority = null; if (name != null) { // TODO: Should this be an exception? if (personService.personExists(name)) { authority = personService.getPerson(name); } } return authority; } /** * Convert authority name to an Alfresco Authority * * @param name * the authority names to convert * @return the Alfresco authorities */ private NodeRef mapNameToAuthority(String name) { NodeRef authority = null; if (name != null) { // TODO: Should this be an exception? if (authorityDAO.authorityExists(name)) { authority = authorityDAO.getAuthorityNodeRefOrNull(name); } } return authority; } /** * Convert Alfresco authority to actor id * * @param authority NodeRef * @return actor id */ private String mapAuthorityToName(NodeRef authority) { String name = null; QName type = nodeService.getType(authority); if (dictionaryService.isSubClass(type, ContentModel.TYPE_PERSON)) { name = (String) nodeService.getProperty(authority, ContentModel.PROP_USERNAME); } else { name = authorityDAO.getAuthorityName(authority); } return name; } /** * Get an I18N Label for a workflow item * * @param displayId * message resource id lookup * @param labelKey * label to lookup (title or description) * @param defaultLabel * default value if not found in message resource bundle * @return the label */ private String getLabel(String displayId, String labelKey, String defaultLabel) { String key = StringUtils.replace(displayId, ":", "_"); key += "." + labelKey; String label = messageService.getMessage(key); return (label == null) ? defaultLabel : label; } /** * Gets the Company Home * * @return company home node ref */ private NodeRef getCompanyHome() { if (tenantService.isEnabled()) { try { return tenantService.getRootNode(nodeService, serviceRegistry.getSearchService(), namespaceService, companyHomePath, nodeService.getRootNode(companyHomeStore)); } catch (RuntimeException re) { String msg = messageService.getMessage(ERR_GET_COMPANY_HOME_INVALID, companyHomePath); throw new IllegalStateException(msg, re); } } else { List<NodeRef> refs = unprotectedSearchService.selectNodes(nodeService.getRootNode(companyHomeStore), companyHomePath, null, namespaceService, false); if (refs.size() != 1) { String msg = messageService.getMessage(ERR_GET_COMPANY_HOME_MULTIPLE, companyHomePath, refs.size()); throw new IllegalStateException(msg); } return refs.get(0); } } // // Workflow Data Object Creation... // /** * Creates a Workflow Path * * @param token * JBoss JBPM Token * @return Workflow Path */ protected WorkflowPath createWorkflowPath(Token token) { 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 WorkflowInstance * @param node WorkflowNode * @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); } /** * Creates a Workflow Node * * @param node * JBoss JBPM Node * @return Workflow Node */ 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(); // TODO: Is there a formal way of determing if task node? boolean isTaskNode = type.equals("TaskNode"); List<Transition> transitions = node.getLeavingTransitions(); List<WorkflowTransition> wfTransitions; if (transitions != null) { wfTransitions = new ArrayList<WorkflowTransition>(transitions.size()); for (Transition transition : transitions) { wfTransitions.add(createWorkflowTransition(transition)); } } else { wfTransitions = Collections.emptyList(); } WorkflowTransition[] transArr = wfTransitions.toArray(new WorkflowTransition[0]); return factory.createNode(name, processName, name, null, type, isTaskNode, transArr); } /** * Create a Workflow Transition * * @param transition * JBoss JBPM Transition * @return Workflow Transition */ protected WorkflowTransition createWorkflowTransition(Transition transition) { if (transition == null) return null; String id = transition.getName(); Node node = transition.getFrom(); boolean isDefault = node.getDefaultLeavingTransition().equals(transition); String title; String description; if (id == null || id.length() == 0) { title = getLabel(DEFAULT_TRANSITION_LABEL, TITLE_LABEL, id); description = getLabel(DEFAULT_TRANSITION_LABEL, DESC_LABEL, title); } else { String nodeName = node.getName(); String processName = node.getProcessDefinition().getName(); title = getLabel(processName + ".node." + nodeName + ".transition." + id, TITLE_LABEL, id); description = getLabel(processName + ".node." + nodeName + ".transition." + id, DESC_LABEL, title); } return new WorkflowTransition(id, title, description, isDefault); } protected WorkflowInstance createWorkflowInstance(ProcessInstance instance) { 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 definition WorkflowDefinition * @param endDate Date * @return Workflow instance */ protected WorkflowInstance createWorkflowInstance(ProcessInstance instance, WorkflowDefinition definition, Date endDate, Map<String, Object> variables) { if (instance == null) return null; String id = Long.toString(instance.getId()); Date startDate = instance.getStart(); boolean isActive = false; if (endDate == null) { isActive = !instance.hasEnded(); endDate = instance.getEnd(); } return factory.createInstance(id, definition, variables, isActive, startDate, endDate); } /** * Creates a Workflow Definition * * @param definition * JBoss Process Definition * @return Workflow Definition */ 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(); WorkflowTaskDefinition startTaskDef = createWorkflowTaskDefinition(startTask); return factory.createDefinition(id, name, version, name, null, startTaskDef); } /** * * Creates a Workflow Task * @param task TaskInstance * @return WorkflowTask */ 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 definition task definition * @param path WorkflowPath * @param properties Map<QName, Serializable> * @return Workflow 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(); WorkflowTaskState state = getWorkflowTaskState(task); return factory.createTask(id, definition, name, null, null, state, path, properties); } /** * Creates a Workflow Task Definition * * @param task * JBoss JBPM Task * @return Workflow Task Definition */ protected WorkflowTaskDefinition createWorkflowTaskDefinition(Task task) { if (task == null) return null; String id = task.getName(); boolean isStart = task.getStartState() != null; Node node = isStart ? task.getStartState() : task.getTaskNode(); WorkflowNode wfNode = createWorkflowNode(node); return factory.createTaskDefinition(id, wfNode, id, isStart); } /** * Creates a Workflow Deployment * * @param compiledDef * compiled JBPM process definition * @return workflow deployment */ protected WorkflowDeployment createWorkflowDeployment(CompiledProcessDefinition compiledDef) { WorkflowDefinition definition = createWorkflowDefinition(compiledDef.def); String[] problems = compiledDef.problems; return factory.createDeployment(definition, problems); } /** * Creates a Workflow Timer * * @param timer * jBPM Timer * @return workflow timer */ protected WorkflowTimer createWorkflowTimer(Timer timer) { if (timer == null) return null; WorkflowPath path = createWorkflowPath(timer.getToken()); WorkflowTask workflowTask = null; TaskInstance taskInstance = timer.getTaskInstance(); if (taskInstance != null) { workflowTask = createWorkflowTask(taskInstance); } return factory.createWorkflowTimer(new Long(timer.getId()).toString(), timer.getName(), timer.getException(), timer.getDueDate(), path, workflowTask); } /** * Get the Workflow Task State for the specified JBoss JBPM Task * * @param task * task * @return task state */ protected WorkflowTaskState getWorkflowTaskState(TaskInstance task) { if (task.hasEnded()) { return WorkflowTaskState.COMPLETED; } else { return WorkflowTaskState.IN_PROGRESS; } } /** * Helper to retrieve the real jBPM Node * * @param node * Node * @return real Node (i.e. the one that's not a Hibernate proxy) */ private Node getRealNode(Node node) { if (node instanceof HibernateProxy) { Node realNode = (Node) ((HibernateProxy) node).getHibernateLazyInitializer().getImplementation(); return realNode; } else { return node; } } @Override protected QName getDefaultStartTaskType() { return WorkflowModel.TYPE_START_TASK; } }