diff --git a/config/alfresco/model/bpmModel.xml b/config/alfresco/model/bpmModel.xml
new file mode 100644
index 0000000000..2229575aaa
--- /dev/null
+++ b/config/alfresco/model/bpmModel.xml
@@ -0,0 +1,280 @@
+
+
+
+
+ Business Process Model
+ Alfresco
+ 1.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+ 2
+ 3
+
+
+
+
+
+
+
+
+ Not Yet Started
+ In Progress
+ On Hold
+ Cancelled
+ Completed
+
+
+
+
+
+ 0
+ 100
+
+
+
+
+
+
+
+
+
+
+
+ Task
+ Task
+ cm:content
+
+
+
+
+
+
+
+
+
+
+
+
+ Task Identifier
+ d:long
+ true
+
+
+
+
+
+
+ Start Date
+ d:date
+ true
+
+
+ End Date
+ d:date
+ true
+
+
+ Due Date
+ d:date
+
+
+
+
+
+
+ Status
+ d:text
+ true
+
+
+
+
+
+ Priority
+ d:int
+
+
+
+
+
+ Percentage Complete
+ d:int
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Pooled Users
+
+ false
+ false
+
+
+ cm:person
+ false
+ true
+
+
+
+
+
+
+ cm:ownable
+
+
+
+
+
+
+
+
+
+ Ad-hoc Task
+ Task assigned by User
+ bpm:task
+
+
+ cm:attachable
+
+
+
+
+
+
+
+
+
+ Workflow Task
+ Task assigned via Workflow
+ bpm:task
+
+
+
+
+
+
+
+
+ Task Context
+ d:noderef
+
+
+
+
+
+
+
+ Workflow Package
+
+ false
+ false
+
+
+ bpm:workflowPackage
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Task Space
+ cm:folder
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Workflow Package
+
+
+
+
+
+ d:noderef
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ false
+
+
+ bpm:taskspace
+ true
+ false
+
+ false
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/workflow-context.xml b/config/alfresco/workflow-context.xml
index 010c82854e..4ce9da7ac3 100644
--- a/config/alfresco/workflow-context.xml
+++ b/config/alfresco/workflow-context.xml
@@ -3,6 +3,18 @@
+
+
+
+
+
+
+
+ alfresco/model/bpmModel.xml
+
+
+
+
diff --git a/source/java/org/alfresco/repo/dictionary/M2ClassDefinition.java b/source/java/org/alfresco/repo/dictionary/M2ClassDefinition.java
index 0fd69bcda6..538c72cfbe 100644
--- a/source/java/org/alfresco/repo/dictionary/M2ClassDefinition.java
+++ b/source/java/org/alfresco/repo/dictionary/M2ClassDefinition.java
@@ -162,8 +162,8 @@ import org.alfresco.service.namespace.QName;
public String toString()
{
StringBuilder sb = new StringBuilder(120);
- sb.append("ClassDef ")
- .append("[ name=").append(name)
+ sb.append("ClassDef")
+ .append("[name=").append(name)
.append("]");
return sb.toString();
}
diff --git a/source/java/org/alfresco/repo/workflow/TaskComponent.java b/source/java/org/alfresco/repo/workflow/TaskComponent.java
index 8fabe54b78..8d5aac2b57 100644
--- a/source/java/org/alfresco/repo/workflow/TaskComponent.java
+++ b/source/java/org/alfresco/repo/workflow/TaskComponent.java
@@ -70,6 +70,25 @@ public interface TaskComponent
*/
public WorkflowTask updateTask(String taskId, Map properties, Map> add, Map> remove);
+ /**
+ * Start the specified Task
+ *
+ * Note: this is an optional task operation. It may be used to track
+ * when work started on a task as well as resume a suspended task.
+ *
+ * @param taskId the task to start
+ * @return the updated task
+ */
+ public WorkflowTask startTask(String taskId);
+
+ /**
+ * Suspend the specified Task
+ *
+ * @param taskId
+ * @return the update task
+ */
+ public WorkflowTask suspendTask(String taskId);
+
/**
* End the Task (i.e. complete the task)
*
@@ -78,6 +97,6 @@ public interface TaskComponent
* @return the updated task
*/
public WorkflowTask endTask(String taskId, String transition);
-
+
}
diff --git a/source/java/org/alfresco/repo/workflow/WorkflowModel.java b/source/java/org/alfresco/repo/workflow/WorkflowModel.java
new file mode 100644
index 0000000000..1b3b5de626
--- /dev/null
+++ b/source/java/org/alfresco/repo/workflow/WorkflowModel.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.workflow;
+
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+
+
+/**
+ * Workflow Model Constants
+ */
+public interface WorkflowModel
+{
+
+ // task constants
+ static final QName TYPE_TASK = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "task");
+ static final QName PROP_TASK_ID = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "taskId");
+ static final QName PROP_START_DATE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "startDate");
+ static final QName PROP_DUE_DATE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "dueDate");
+ static final QName PROP_COMPLETION_DATE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "completionDate");
+ static final QName PROP_PRIORITY = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "priority");
+ static final QName PROP_STATUS = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "status");
+ static final QName PROP_PERCENT_COMPLETE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "percentComplete");
+ static final QName ASSOC_POOLED_ACTORS = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "pooledActors");
+
+ // workflow task contstants
+ static final QName TYPE_WORKFLOW_TASK = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowTask");
+ static final QName PROP_CONTEXT = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "context");
+ static final QName ASSOC_PACKAGE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "package");
+
+}
diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java
index 8f58734b9c..8ef77d2a9d 100644
--- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java
+++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java
@@ -19,17 +19,28 @@ package org.alfresco.repo.workflow.jbpm;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.Map.Entry;
+import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.workflow.BPMEngine;
import org.alfresco.repo.workflow.TaskComponent;
import org.alfresco.repo.workflow.WorkflowComponent;
import org.alfresco.repo.workflow.WorkflowDefinitionComponent;
+import org.alfresco.repo.workflow.WorkflowModel;
+import org.alfresco.service.cmr.dictionary.AspectDefinition;
+import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
+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.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowException;
import org.alfresco.service.cmr.workflow.WorkflowInstance;
@@ -43,7 +54,6 @@ import org.alfresco.service.namespace.QName;
import org.hibernate.proxy.HibernateProxy;
import org.jbpm.JbpmContext;
import org.jbpm.JbpmException;
-import org.jbpm.context.exe.ContextInstance;
import org.jbpm.db.GraphSession;
import org.jbpm.db.TaskMgmtSession;
import org.jbpm.graph.def.Node;
@@ -72,6 +82,8 @@ public class JBPMEngine extends BPMEngine
// Implementation dependencies
protected DictionaryService dictionaryService;
protected NamespaceService namespaceService;
+ protected NodeService nodeService;
+ protected PersonService personService;
private JbpmTemplate jbpmTemplate;
/**
@@ -104,6 +116,26 @@ public class JBPMEngine extends BPMEngine
this.namespaceService = namespaceService;
}
+ /**
+ * Sets the Node Service
+ *
+ * @param nodeService
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ /**
+ * Sets the Person Service
+ *
+ * @param personService
+ */
+ public void setPersonService(PersonService personService)
+ {
+ this.personService = personService;
+ }
+
//
// Workflow Definition...
@@ -114,8 +146,8 @@ public class JBPMEngine extends BPMEngine
*/
public WorkflowDefinition deployDefinition(InputStream workflowDefinition)
{
- // TODO Auto-generated method stub
- return null;
+ // TODO
+ throw new UnsupportedOperationException();
}
/* (non-Javadoc)
@@ -123,8 +155,8 @@ public class JBPMEngine extends BPMEngine
*/
public void undeployDefinition(String workflowDefinitionId)
{
- // TODO Auto-generated method stub
-
+ // TODO
+ throw new UnsupportedOperationException();
}
/* (non-Javadoc)
@@ -465,10 +497,121 @@ public class JBPMEngine extends BPMEngine
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.TaskComponent#updateTask(java.lang.String, java.util.Map, java.util.Map, java.util.Map)
*/
- public WorkflowTask updateTask(String taskId, Map properties, Map> add, Map> remove)
+ public WorkflowTask updateTask(final String taskId, final Map properties, final Map> add, final Map> remove)
{
- // TODO
- throw new UnsupportedOperationException();
+ try
+ {
+ return (WorkflowTask) jbpmTemplate.execute(new JbpmCallback()
+ {
+ public Object doInJbpm(JbpmContext context)
+ {
+ // retrieve task
+ TaskMgmtSession taskSession = context.getTaskMgmtSession();
+ TaskInstance taskInstance = taskSession.loadTaskInstance(getJbpmId(taskId));
+
+ // create properties to set on task instance
+ Map newProperties = properties;
+ if (newProperties == null && (add != null || remove != null))
+ {
+ newProperties = new HashMap(10);
+ }
+
+ if (add != null || remove != null)
+ {
+ Map existingProperties = getTaskProperties(taskInstance);
+
+ if (add != null)
+ {
+ // add new associations
+ for (Entry> toAdd : add.entrySet())
+ {
+ // retrieve existing list of noderefs for association
+ List existingAdd = (List)newProperties.get(toAdd.getKey());
+ if (existingAdd == null)
+ {
+ existingAdd = (List)existingProperties.get(toAdd.getKey());
+ }
+
+ // make the additions
+ if (existingAdd == null)
+ {
+ newProperties.put(toAdd.getKey(), (Serializable)toAdd.getValue());
+ }
+ else
+ {
+ for (NodeRef nodeRef : (List)toAdd.getValue())
+ {
+ if (!(existingAdd.contains(nodeRef)))
+ {
+ existingAdd.add(nodeRef);
+ }
+ }
+ }
+ }
+ }
+
+ if (remove != null)
+ {
+ // add new associations
+ for (Entry> toRemove: remove.entrySet())
+ {
+ // retrieve existing list of noderefs for association
+ List existingRemove = (List)newProperties.get(toRemove.getKey());
+ if (existingRemove == null)
+ {
+ existingRemove = (List)existingProperties.get(toRemove.getKey());
+ }
+
+ // make the subtractions
+ if (existingRemove != null)
+ {
+ for (NodeRef nodeRef : (List)toRemove.getValue())
+ {
+ existingRemove.remove(nodeRef);
+ }
+ }
+ }
+ }
+ }
+
+ // update the task
+ if (newProperties != null)
+ {
+ 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)
+ {
+ throw new WorkflowException("Failed to update workflow task '" + taskId + "'", e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.TaskComponent#startTask(java.lang.String)
+ */
+ public WorkflowTask startTask(String taskId)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.TaskComponent#suspendTask(java.lang.String)
+ */
+ public WorkflowTask suspendTask(String taskId)
+ {
+ // TODO Auto-generated method stub
+ return null;
}
/* (non-Javadoc)
@@ -526,45 +669,51 @@ public class JBPMEngine extends BPMEngine
return null;
}
- /**
- * Sets Properties of Task
- *
- * @param instance task instance
- * @param properties properties to set
- */
- protected void setTaskProperties(TaskInstance instance, Map properties)
- {
- if (properties == null)
- {
- return;
- }
-
- // TODO: Use Dictionary to drive mapping
-
- // TODO: Determine if NodeRefs and collection of NodeRefs need to be converted to String
-
- ContextInstance context = instance.getContextInstance();
- for (Entry entry : properties.entrySet())
- {
- String name = null;
- QName qname = entry.getKey();
- if (qname.getNamespaceURI().equals(NamespaceService.DEFAULT_URI))
- {
- name = qname.getLocalName();
- }
- else
- {
- name = qname.toPrefixString(namespaceService);
- }
- context.setVariable(name, entry.getValue());
- }
- }
-
//
// Helpers...
//
+ /**
+ * Gets the Task definition of the specified Task
+ *
+ * @param task the task
+ * @return the task definition
+ */
+ private TypeDefinition getTaskDefinition(Task task)
+ {
+ // TODO: Extend jBPM task instance to include dictionary definition qname?
+ QName typeName = QName.createQName(task.getName(), namespaceService);
+ TypeDefinition typeDef = dictionaryService.getType(typeName);
+ if (typeDef == null)
+ {
+ typeDef = dictionaryService.getType(WorkflowModel.TYPE_WORKFLOW_TASK);
+ if (typeDef == null)
+ {
+ throw new WorkflowException("Failed to find type definition '" + WorkflowModel.TYPE_WORKFLOW_TASK + "'");
+ }
+ }
+ return typeDef;
+ }
+
+ /**
+ * Convert the specified Type definition to an anonymous Type definition.
+ *
+ * This collapses all mandatory aspects into a single Type definition.
+ *
+ * @param typeDef the type definition
+ * @return the anonymous type definition
+ */
+ private TypeDefinition getAnonymousTaskDefinition(TypeDefinition typeDef)
+ {
+ List aspects = typeDef.getDefaultAspects();
+ List aspectNames = new ArrayList(aspects.size());
+ for (AspectDefinition aspect : aspects)
+ {
+ aspectNames.add(aspect.getName());
+ }
+ return dictionaryService.getAnonymousType(typeDef.getName(), aspectNames);
+ }
/**
* Get JBoss JBPM Id from Engine Global Id
@@ -612,6 +761,214 @@ public class JBPMEngine extends BPMEngine
return token;
}
+ /**
+ * Sets Properties of Task
+ *
+ * @param instance task instance
+ * @param properties properties to set
+ */
+ @SuppressWarnings("unchecked")
+ protected Map getTaskProperties(TaskInstance instance)
+ {
+ // establish task definition
+ TypeDefinition taskDef = getAnonymousTaskDefinition(getTaskDefinition(instance.getTask()));
+ Map taskAssocs = taskDef.getAssociations();
+
+ // map arbitrary task variables
+ Map properties = new HashMap(10);
+ Map vars = instance.getVariablesLocally();
+ for (Entry entry : vars.entrySet())
+ {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+
+ // perform data conversions
+ // NOTE: Only convert Authority name to NodeRef for now
+ QName qname = QName.createQName(key);
+ AssociationDefinition assocDef = taskAssocs.get(qname);
+ if (assocDef != null && assocDef.getTargetClass().equals(ContentModel.TYPE_PERSON))
+ {
+ // TODO: Also support group authorities
+ if (!(value instanceof String[]))
+ {
+ throw new WorkflowException("Task variable '" + qname + "' value is invalid format");
+ }
+ value = mapNameToAuthority((String[])value);
+ }
+
+ // place task variable in map to return
+ properties.put(qname, (Serializable)value);
+ }
+
+ // map jBPM task instance fields to properties
+ properties.put(WorkflowModel.PROP_TASK_ID, instance.getId());
+ 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_OWNER, instance.getActorId());
+
+ // map jBPM task instance collections to associations
+ Set pooledActors = instance.getPooledActors();
+ if (pooledActors != null)
+ {
+ String[] pooledActorIds = new String[pooledActors.size()];
+ pooledActors.toArray(pooledActorIds);
+ List pooledActorNodeRefs = mapNameToAuthority(pooledActorIds);
+ properties.put(WorkflowModel.ASSOC_POOLED_ACTORS, (Serializable)pooledActorNodeRefs);
+ }
+
+ return properties;
+ }
+
+ /**
+ * Sets Properties of Task
+ *
+ * @param instance task instance
+ * @param properties properties to set
+ */
+ protected void setTaskProperties(TaskInstance instance, Map properties)
+ {
+ if (properties == null)
+ {
+ return;
+ }
+
+ // establish task definition
+ TypeDefinition taskDef = getAnonymousTaskDefinition(getTaskDefinition(instance.getTask()));
+ Map taskProperties = taskDef.getProperties();
+ Map taskAssocs = taskDef.getAssociations();
+
+ // map each parameter to task
+ for (Entry 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;
+ }
+
+ // map property to specific jBPM task instance field
+ if (key.equals(WorkflowModel.PROP_DUE_DATE))
+ {
+ if (!(value instanceof Date))
+ {
+ throw new WorkflowException("Task due date '" + value + "' is invalid");
+ }
+ instance.setDueDate((Date)value);
+ continue;
+ }
+ else if (key.equals(WorkflowModel.PROP_PRIORITY))
+ {
+ if (!(value instanceof Integer))
+ {
+ throw new WorkflowException("Task priority '" + value + "' is invalid");
+ }
+ instance.setPriority((Integer)value);
+ continue;
+ }
+ else if (key.equals(ContentModel.PROP_OWNER))
+ {
+ if (!(value instanceof String))
+ {
+ throw new WorkflowException("Task owner '" + value + "' is invalid");
+ }
+ instance.setActorId((String)value);
+ continue;
+ }
+ }
+ else
+ {
+ // determine if writing association
+ AssociationDefinition assocDef = taskAssocs.get(key);
+ if (assocDef != null)
+ {
+ // if association is to people, map them to authority names
+ // TODO: support group authorities
+ if (assocDef.getTargetClass().equals(ContentModel.TYPE_PERSON))
+ {
+ value = mapAuthorityToName((List)value);
+ }
+
+ // map association to specific jBPM task instance field
+ if (key.equals(WorkflowModel.ASSOC_POOLED_ACTORS))
+ {
+ instance.setPooledActors((String[])value);
+ continue;
+ }
+ }
+ }
+
+ // no specific mapping to jBPM task has been established, so place into
+ // the generic task variable bag
+ String name = null;
+ if (key.getNamespaceURI().equals(NamespaceService.DEFAULT_URI))
+ {
+ name = key.getLocalName();
+ }
+ else
+ {
+ name = key.toString();
+ }
+ instance.setVariableLocally(name, value);
+ }
+ }
+
+ /**
+ * Convert a list of Alfresco Authorities to a list of authority Names
+ *
+ * @param authorities the authorities to convert
+ * @return the authority names
+ */
+ private String[] mapAuthorityToName(List authorities)
+ {
+ String[] names = null;
+ if (authorities != null)
+ {
+ names = new String[authorities.size()];
+ int i = 0;
+ for (NodeRef person : authorities)
+ {
+ String name = (String)nodeService.getProperty(person, ContentModel.PROP_USERNAME);
+ names[i++] = name;
+ }
+ }
+ return names;
+ }
+
+ /**
+ * Convert a list of authority Names to Alfresco Authorities
+ *
+ * @param names the authority names to convert
+ * @return the Alfresco authorities
+ */
+ private List mapNameToAuthority(String[] names)
+ {
+ List authorities = null;
+ if (names != null)
+ {
+ authorities = new ArrayList(names.length);
+ for (String name : names)
+ {
+ // TODO: Should this be an exception?
+ if (personService.personExists(name))
+ {
+ authorities.add(personService.getPerson(name));
+ }
+ }
+ }
+ return authorities;
+ }
+
//
// Workflow Data Object Creation...
@@ -714,12 +1071,10 @@ public class JBPMEngine extends BPMEngine
workflowTask.path = createWorkflowPath(task.getToken());
workflowTask.state = getWorkflowTaskState(task);
workflowTask.definition = createWorkflowTaskDefinition(task.getTask());
-
- // TODO: Properties and Associations
-
+ workflowTask.properties = getTaskProperties(task);
return workflowTask;
}
-
+
/**
* Creates a Workflow Task Definition
*
@@ -728,11 +1083,9 @@ public class JBPMEngine extends BPMEngine
*/
protected WorkflowTaskDefinition createWorkflowTaskDefinition(Task task)
{
- // TODO: Extend jBPM task instance to include dictionary definition qname
WorkflowTaskDefinition taskDef = new WorkflowTaskDefinition();
taskDef.id = task.getName();
- QName typeName = QName.createQName(taskDef.id, namespaceService);
- taskDef.metadata = dictionaryService.getType(typeName);
+ taskDef.metadata = getTaskDefinition(task);
return taskDef;
}
diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java
index 2145f4b3ea..36fcf875e2 100644
--- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java
+++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java
@@ -17,14 +17,19 @@
package org.alfresco.repo.workflow.jbpm;
import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.alfresco.model.ContentModel;
import org.alfresco.repo.workflow.BPMEngineRegistry;
import org.alfresco.repo.workflow.TaskComponent;
import org.alfresco.repo.workflow.WorkflowComponent;
import org.alfresco.repo.workflow.WorkflowDefinitionComponent;
+import org.alfresco.repo.workflow.WorkflowModel;
+import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowException;
import org.alfresco.service.cmr.workflow.WorkflowInstance;
@@ -55,6 +60,8 @@ public class JBPMEngineTest extends BaseSpringTest
workflowDefinitionComponent = registry.getWorkflowDefinitionComponent("jbpm");
workflowComponent = registry.getWorkflowComponent("jbpm");
taskComponent = registry.getTaskComponent("jbpm");
+
+
}
@@ -97,6 +104,121 @@ public class JBPMEngineTest extends BaseSpringTest
}
+ public void testStartWorkflowParameters()
+ {
+ WorkflowDefinition workflowDef = getTestDefinition();
+
+ Map params = new HashMap();
+ params.put(WorkflowModel.PROP_TASK_ID, 3); // protected - shouldn't be written
+ params.put(WorkflowModel.PROP_DUE_DATE, new Date()); // task instance field
+ params.put(WorkflowModel.PROP_PRIORITY, 1); // task instance field
+ params.put(WorkflowModel.PROP_PERCENT_COMPLETE, 10); // context variable
+ params.put(QName.createQName("", "Message"), "Hello World"); // context variable outside of task definition
+ params.put(QName.createQName("", "Array"), new String[] { "one", "two" }); // context variable outside of task definition
+ params.put(QName.createQName("", "NodeRef"), new NodeRef("workspace://1/1001")); // context variable outside of task definition
+ params.put(ContentModel.PROP_OWNER, "admin"); // task assignment
+
+ WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, params);
+ assertNotNull(path);
+ assertTrue(path.id.endsWith("::/"));
+ assertNotNull(path.node);
+ assertNotNull(path.instance);
+ assertEquals(workflowDef.id, path.instance.definition.id);
+ List tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
+ assertNotNull(tasks1);
+ assertEquals(1, tasks1.size());
+
+ WorkflowTask task = tasks1.get(0);
+ assertTrue(task.properties.containsKey(WorkflowModel.PROP_TASK_ID));
+ assertTrue(task.properties.containsKey(WorkflowModel.PROP_DUE_DATE));
+ assertTrue(task.properties.containsKey(WorkflowModel.PROP_PRIORITY));
+ assertTrue(task.properties.containsKey(WorkflowModel.PROP_PERCENT_COMPLETE));
+ assertTrue(task.properties.containsKey(ContentModel.PROP_OWNER));
+ }
+
+
+ public void testUpdateTask()
+ {
+ WorkflowDefinition workflowDef = getTestDefinition();
+
+ Map params = new HashMap();
+ params.put(WorkflowModel.PROP_TASK_ID, 3); // protected - shouldn't be written
+ params.put(WorkflowModel.PROP_DUE_DATE, new Date()); // task instance field
+ params.put(WorkflowModel.PROP_PRIORITY, 1); // task instance field
+ params.put(WorkflowModel.PROP_PERCENT_COMPLETE, 10); // context variable
+ params.put(QName.createQName("", "Message"), "Hello World"); // context variable outside of task definition
+ params.put(QName.createQName("", "Array"), new String[] { "one", "two" }); // context variable outside of task definition
+ params.put(QName.createQName("", "NodeRef"), new NodeRef("workspace://1/1001")); // context variable outside of task definition
+ params.put(ContentModel.PROP_OWNER, "admin"); // task assignment
+
+ WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, params);
+ assertNotNull(path);
+ assertTrue(path.id.endsWith("::/"));
+ assertNotNull(path.node);
+ assertNotNull(path.instance);
+ assertEquals(workflowDef.id, path.instance.definition.id);
+ List tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
+ assertNotNull(tasks1);
+ assertEquals(1, tasks1.size());
+
+ WorkflowTask task = tasks1.get(0);
+ assertTrue(task.properties.containsKey(WorkflowModel.PROP_TASK_ID));
+ assertTrue(task.properties.containsKey(WorkflowModel.PROP_DUE_DATE));
+ assertTrue(task.properties.containsKey(WorkflowModel.PROP_PRIORITY));
+ assertTrue(task.properties.containsKey(WorkflowModel.PROP_PERCENT_COMPLETE));
+ assertTrue(task.properties.containsKey(ContentModel.PROP_OWNER));
+
+ // update with null parameters
+ try
+ {
+ WorkflowTask taskU1 = taskComponent.updateTask(task.id, null, null, null);
+ assertNotNull(taskU1);
+ }
+ catch(Throwable e)
+ {
+ fail("Task update failed with null parameters");
+ }
+
+ // update property value
+ Map updateProperties2 = new HashMap();
+ updateProperties2.put(WorkflowModel.PROP_PERCENT_COMPLETE, 100);
+ WorkflowTask taskU2 = taskComponent.updateTask(task.id, updateProperties2, null, null);
+ assertEquals(100, taskU2.properties.get(WorkflowModel.PROP_PERCENT_COMPLETE));
+
+ // add to assocation
+ QName assocName = QName.createQName("", "TestAssoc");
+ List toAdd = new ArrayList();
+ toAdd.add(new NodeRef("workspace://1/1001"));
+ toAdd.add(new NodeRef("workspace://1/1002"));
+ toAdd.add(new NodeRef("workspace://1/1003"));
+ Map> addAssocs = new HashMap>();
+ addAssocs.put(assocName, toAdd);
+ WorkflowTask taskU3 = taskComponent.updateTask(task.id, null, addAssocs, null);
+ assertNotNull(taskU3.properties.get(assocName));
+ assertEquals(3, ((List)taskU3.properties.get(assocName)).size());
+
+ // add to assocation again
+ List toAddAgain = new ArrayList();
+ toAddAgain.add(new NodeRef("workspace://1/1004"));
+ toAddAgain.add(new NodeRef("workspace://1/1005"));
+ Map> addAssocsAgain = new HashMap>();
+ addAssocsAgain.put(assocName, toAddAgain);
+ WorkflowTask taskU4 = taskComponent.updateTask(task.id, null, addAssocsAgain, null);
+ assertNotNull(taskU4.properties.get(assocName));
+ assertEquals(5, ((List)taskU4.properties.get(assocName)).size());
+
+ // remove assocation
+ List toRemove = new ArrayList();
+ toRemove.add(new NodeRef("workspace://1/1002"));
+ toRemove.add(new NodeRef("workspace://1/1003"));
+ Map> removeAssocs = new HashMap>();
+ removeAssocs.put(assocName, toRemove);
+ WorkflowTask taskU5 = taskComponent.updateTask(task.id, null, null, removeAssocs);
+ assertNotNull(taskU5.properties.get(assocName));
+ assertEquals(3, ((List)taskU5.properties.get(assocName)).size());
+ }
+
+
public void testGetWorkflowInstances()
{
WorkflowDefinition workflowDef = getTestDefinition();
diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowTask.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowTask.java
index 07fe5f2411..068044a66b 100644
--- a/source/java/org/alfresco/service/cmr/workflow/WorkflowTask.java
+++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowTask.java
@@ -17,10 +17,8 @@
package org.alfresco.service.cmr.workflow;
import java.io.Serializable;
-import java.util.List;
import java.util.Map;
-import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
@@ -51,16 +49,13 @@ public class WorkflowTask
/** Task Properties as described by Task Definition */
public Map properties;
- /** Task Associations as described by Task Definition */
- public Map> associations;
-
-
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString()
{
- return "WorkflowTask[id=" + id + ",name=" + name + ",state=" + state + ",def=" + definition + ",path=" + path.toString() + "]";
+ String propCount = (properties == null) ? "null" : "" + properties.size();
+ return "WorkflowTask[id=" + id + ",name=" + name + ",state=" + state + ",props=" + propCount + ",def=" + definition + ",path=" + path.toString() + "]";
}
}
diff --git a/source/java/org/alfresco/service/namespace/NamespaceService.java b/source/java/org/alfresco/service/namespace/NamespaceService.java
index 9ef59abf26..cfd2c7a350 100644
--- a/source/java/org/alfresco/service/namespace/NamespaceService.java
+++ b/source/java/org/alfresco/service/namespace/NamespaceService.java
@@ -66,6 +66,12 @@ public interface NamespaceService extends NamespacePrefixResolver
/** Application Model Prefix */
public static final String APP_MODEL_PREFIX = "app";
+ /** Business Process Model URI */
+ public static final String BPM_MODEL_1_0_URI = "http://www.alfresco.org/model/bpm/1.0";
+
+ /** Business Process Model Prefix */
+ public static final String BPM_MODEL_PREFIX = "bpm";
+
/** Alfresco View Namespace URI */
public static final String REPOSITORY_VIEW_1_0_URI = "http://www.alfresco.org/view/repository/1.0";