Workflow:

- Pooled task support

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4782 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
David Caruana 2007-01-10 15:40:59 +00:00
parent 280c197d4d
commit ff9d1f598f
14 changed files with 336 additions and 322 deletions

View File

@ -27,7 +27,9 @@
</start-state> </start-state>
<swimlane name="reviewer"> <swimlane name="reviewer">
<assignment actor-id="#{bpm_assignee.properties['cm:userName']}" /> <assignment class="org.alfresco.repo.workflow.jbpm.AlfrescoAssignment">
<actor>#{bpm_assignee}</actor>
</assignment>
</swimlane> </swimlane>
<task-node name="review"> <task-node name="review">

View File

@ -1,160 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<model name="wf:workflowmodel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
<imports>
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
<import uri="http://www.alfresco.org/model/bpm/1.0" prefix="bpm"/>
</imports>
<namespaces>
<namespace uri="http://www.alfresco.org/model/workflow/1.0" prefix="wf"/>
</namespaces>
<types>
<!-- -->
<!-- Basic Review & Approve Tasks -->
<!-- -->
<type name="wf:submitReviewTask">
<title>Submit Review Task</title>
<parent>bpm:workflowTask</parent>
<properties>
<property name="wf:reviewDueDate">
<title>Review Due Date</title>
<type>d:date</type>
</property>
<property name="wf:reviewPriority">
<title>Review Priority</title>
<type>d:int</type>
<default>2</default>
<constraints>
<constraint ref="bpm:allowedPriority"/>
</constraints>
</property>
</properties>
<associations>
<association name="wf:reviewer">
<title>Reviewer</title>
<source>
<mandatory>false</mandatory>
<many>false</many>
</source>
<target>
<class>cm:person</class>
<mandatory>true</mandatory>
<many>false</many>
</target>
</association>
</associations>
<overrides>
<property name="bpm:packageActionGroup">
<default>workflow_collection_actions</default>
</property>
<property name="bpm:packageItemActionGroup">
<default>workflow_item_collection_actions</default>
</property>
</overrides>
</type>
<type name="wf:reviewTask">
<title>Review Task</title>
<parent>bpm:workflowTask</parent>
<overrides>
<property name="bpm:packageItemActionGroup">
<default>workflow_item_edit_actions</default>
</property>
</overrides>
</type>
<!-- -->
<!-- Adhoc Tasks -->
<!-- -->
<type name="wf:baseAdhocTask">
<parent>bpm:workflowTask</parent>
<properties>
<property name="wf:adhocDescription">
<title>Description</title>
<type>d:text</type>
</property>
</properties>
</type>
<type name="wf:submitAdhocTask">
<title>Submit Adhoc Task</title>
<parent>wf:baseAdhocTask</parent>
<properties>
<property name="wf:adhocDueDate">
<title>Due Date</title>
<type>d:date</type>
</property>
<property name="wf:adhocPriority">
<title>Priority</title>
<type>d:int</type>
<default>2</default>
<constraints>
<constraint ref="bpm:allowedPriority"/>
</constraints>
</property>
<property name="wf:notifyMe">
<title>Email Notification</title>
<type>d:boolean</type>
</property>
</properties>
<associations>
<association name="wf:assignee">
<title>Assignee</title>
<source>
<mandatory>false</mandatory>
<many>false</many>
</source>
<target>
<class>cm:person</class>
<mandatory>true</mandatory>
<many>false</many>
</target>
</association>
</associations>
</type>
<type name="wf:adhocTask">
<title>Adhoc Task</title>
<parent>wf:baseAdhocTask</parent>
</type>
<type name="wf:completedAdhocTask">
<title>Completed Adhoc Task</title>
<parent>wf:baseAdhocTask</parent>
</type>
</types>
</model>

View File

@ -10,7 +10,9 @@
</start-state> </start-state>
<swimlane name="assignee"> <swimlane name="assignee">
<assignment actor-id="#{bpm_assignee.properties['cm:userName']}"/> <assignment class="org.alfresco.repo.workflow.jbpm.AlfrescoAssignment">
<actor>#{bpm_assignee}</actor>
</assignment>
</swimlane> </swimlane>
<task-node name="adhoc"> <task-node name="adhoc">

View File

@ -27,13 +27,9 @@
<task-node name="review"> <task-node name="review">
<task name="wf:reviewTask"> <task name="wf:reviewTask">
<event type="task-create"> <assignment class="org.alfresco.repo.workflow.jbpm.AlfrescoAssignment">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript"> <actor>#{reviewer}</actor>
<script> </assignment>
taskInstance.actorId = reviewer.properties.userName;
</script>
</action>
</event>
</task> </task>
<transition name="reject" to="endreview" /> <transition name="reject" to="endreview" />
<transition name="approve" to="endreview"> <transition name="approve" to="endreview">

View File

@ -10,7 +10,9 @@
</start-state> </start-state>
<swimlane name="reviewer"> <swimlane name="reviewer">
<assignment actor-id="#{bpm_assignee.properties['cm:userName']}" /> <assignment class="org.alfresco.repo.workflow.jbpm.AlfrescoAssignment">
<actor>#{bpm_assignee}</actor>
</assignment>
</swimlane> </swimlane>
<task-node name="review"> <task-node name="review">

View File

@ -55,13 +55,9 @@
<task-node name="serialreview"> <task-node name="serialreview">
<task name="wcmwf:reviewTask"> <task name="wcmwf:reviewTask">
<event type="task-create"> <assignment class="org.alfresco.repo.workflow.jbpm.AlfrescoAssignment">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript"> <actor>#{bpm_assignees.get(wcmwf_approveCnt)}</actor>
<script> </assignment>
taskInstance.actorId = bpm_assignees.get(wcmwf_approveCnt).properties.userName;
</script>
</action>
</event>
</task> </task>
<transition name="reject" to="endreview" /> <transition name="reject" to="endreview" />
@ -90,13 +86,9 @@
<task-node name="parallelreview"> <task-node name="parallelreview">
<task name="wcmwf:reviewTask"> <task name="wcmwf:reviewTask">
<event type="task-create"> <assignment class="org.alfresco.repo.workflow.jbpm.AlfrescoAssignment">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript"> <actor>#{reviewer}</actor>
<script> </assignment>
taskInstance.actorId = reviewer.properties["cm:userName"];
</script>
</action>
</event>
</task> </task>
<transition name="reject" to="joinparallelreview" /> <transition name="reject" to="joinparallelreview" />
<transition name="approve" to="joinparallelreview"> <transition name="approve" to="joinparallelreview">

View File

@ -443,6 +443,17 @@ public class WorkflowInterpreter
out.println("id: " + task.id + " , name " + task.name + " , properties: " + task.properties.size() + " , workflow: " + task.path.instance.id + " , path: " + task.path.id); out.println("id: " + task.id + " , name " + task.name + " , properties: " + task.properties.size() + " , workflow: " + task.path.instance.id + " , path: " + task.path.id);
} }
} }
else if (command[2].equals("pooled"))
{
out.println(AuthenticationUtil.getCurrentUserName() + ":");
List<WorkflowTask> tasks = workflowService.getPooledTasks(AuthenticationUtil.getCurrentUserName());
for (WorkflowTask task : tasks)
{
out.println("id: " + task.id + " , name " + task.name + " , properties: " + task.properties.size() + " , workflow: " + task.path.instance.id + " , path: " + task.path.id);
}
}
else else
{ {
return "Syntax Error.\n"; return "Syntax Error.\n";

View File

@ -0,0 +1,173 @@
/*
* 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.jbpm;
import java.util.Collection;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.workflow.WorkflowException;
import org.dom4j.Element;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.jpdl.el.impl.JbpmExpressionEvaluator;
import org.jbpm.taskmgmt.def.AssignmentHandler;
import org.jbpm.taskmgmt.exe.Assignable;
/**
* Assignment Handler for assigning Alfresco People and Groups to Tasks
* and Swimlanes
*
* @author davidc
*/
public class AlfrescoAssignment implements AssignmentHandler
{
private static final long serialVersionUID = 1025667849552265719L;
private Element actor;
private Element pooledactors;
/* (non-Javadoc)
* @see org.jbpm.taskmgmt.def.AssignmentHandler#assign(org.jbpm.taskmgmt.exe.Assignable, org.jbpm.graph.exe.ExecutionContext)
*/
public void assign(Assignable assignable, ExecutionContext executionContext) throws Exception
{
if (actor == null && pooledactors == null)
{
throw new WorkflowException("no actor or pooled actors has been specified");
}
//
// extract actor
//
String assignedActor = null;
if (actor != null)
{
String actorValStr = actor.getTextTrim();
if (actorValStr != null && actorValStr.length() > 0)
{
if (actorValStr.startsWith("#{"))
{
Object eval = JbpmExpressionEvaluator.evaluate(actorValStr, executionContext);
if (eval == null)
{
throw new WorkflowException("actor expression '" + actorValStr + "' evaluates to null");
}
if (eval instanceof String)
{
assignedActor = (String)eval;
}
else if (eval instanceof JBPMNode)
{
JBPMNode node = (JBPMNode)eval;
if (!node.getType().equals(ContentModel.TYPE_PERSON))
{
throw new WorkflowException("actor expression does not evaluate to a person");
}
assignedActor = (String)node.getProperties().get(ContentModel.PROP_USERNAME);
}
else
{
throw new WorkflowException("actor expression does not evaluate to a person");
}
}
else
{
assignedActor = actorValStr;
}
}
}
//
// extract pooled actors
//
String[] assignedPooledActors = null;
if (pooledactors != null)
{
String pooledactorValStr = pooledactors.getTextTrim();
if (pooledactorValStr != null && pooledactorValStr.length() > 0)
{
if (pooledactorValStr.startsWith("#{"))
{
Object eval = JbpmExpressionEvaluator.evaluate(pooledactorValStr, executionContext);
if (eval == null)
{
throw new WorkflowException("pooledactors expression '" + pooledactorValStr + "' evaluates to null");
}
if (eval instanceof Collection)
{
Collection coll = (Collection)eval;
assignedPooledActors = new String[coll.size()];
int i = 0;
for (Object obj : coll)
{
if (!(obj instanceof JBPMNode))
{
throw new WorkflowException("pooledactors does not refer to a collection of people");
}
JBPMNode node = (JBPMNode)obj;
if (!node.getType().equals(ContentModel.TYPE_PERSON))
{
throw new WorkflowException("pooledactors expression does not evaluate to a collection of people");
}
assignedPooledActors[i++] = (String)node.getProperties().get(ContentModel.PROP_USERNAME);
}
}
else if (eval instanceof JBPMNode)
{
JBPMNode node = (JBPMNode)eval;
if (node.getType().equals(ContentModel.TYPE_PERSON))
{
assignedPooledActors[0] = (String)node.getProperties().get(ContentModel.PROP_USERNAME);
}
// TODO: Support Group
else
{
throw new WorkflowException("pooledactors expression does not evaluate to a collection of people");
}
}
else
{
throw new WorkflowException("pooledactor expression does not evaluate to a group or collection of people");
}
}
else
{
assignedPooledActors[0] = pooledactorValStr;
}
}
}
//
// make the assignment
//
if (assignedActor != null)
{
assignable.setActorId(assignedActor);
}
if (assignedPooledActors != null)
{
assignable.setPooledActors(assignedPooledActors);
}
}
}

View File

@ -82,6 +82,7 @@ import org.jbpm.jpdl.par.ProcessArchive;
import org.jbpm.jpdl.xml.JpdlXmlReader; import org.jbpm.jpdl.xml.JpdlXmlReader;
import org.jbpm.jpdl.xml.Problem; import org.jbpm.jpdl.xml.Problem;
import org.jbpm.taskmgmt.def.Task; import org.jbpm.taskmgmt.def.Task;
import org.jbpm.taskmgmt.exe.PooledActor;
import org.jbpm.taskmgmt.exe.TaskInstance; import org.jbpm.taskmgmt.exe.TaskInstance;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springmodules.workflow.jbpm31.JbpmCallback; import org.springmodules.workflow.jbpm31.JbpmCallback;
@ -1369,9 +1370,9 @@ public class JBPMEngine extends BPMEngine
if (pooledActors != null) if (pooledActors != null)
{ {
List<NodeRef> pooledNodeRefs = new ArrayList<NodeRef>(pooledActors.size()); List<NodeRef> pooledNodeRefs = new ArrayList<NodeRef>(pooledActors.size());
for (String pooledActor : (Set<String>)pooledActors) for (PooledActor pooledActor : (Set<PooledActor>)pooledActors)
{ {
NodeRef pooledNodeRef = mapNameToAuthority(pooledActor); NodeRef pooledNodeRef = mapNameToAuthority(pooledActor.getActorId());
if (pooledNodeRef != null) if (pooledNodeRef != null)
{ {
pooledNodeRefs.add(pooledNodeRef); pooledNodeRefs.add(pooledNodeRef);
@ -1746,7 +1747,7 @@ public class JBPMEngine extends BPMEngine
if (isMany) if (isMany)
{ {
// convert single node ref to list of node refs // convert single node ref to list of node refs
JBPMNodeList values = new JBPMNodeList(); JBPMNodeList values = new JBPMNodeList(serviceRegistry);
values.add(new JBPMNode((NodeRef)value, serviceRegistry)); values.add(new JBPMNode((NodeRef)value, serviceRegistry));
value = (Serializable)values; value = (Serializable)values;
} }
@ -1759,7 +1760,7 @@ public class JBPMEngine extends BPMEngine
{ {
if (isMany) if (isMany)
{ {
JBPMNodeList values = new JBPMNodeList(); JBPMNodeList values = new JBPMNodeList(serviceRegistry);
for (NodeRef nodeRef : (List<NodeRef>)value) for (NodeRef nodeRef : (List<NodeRef>)value)
{ {
values.add(new JBPMNode(nodeRef, serviceRegistry)); values.add(new JBPMNode(nodeRef, serviceRegistry));

View File

@ -1,9 +1,112 @@
/*
* 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.jbpm; package org.alfresco.repo.workflow.jbpm;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QNameMap;
/**
* List of jBPM Nodes
*
* @author davidc
*/
public class JBPMNodeList extends ArrayList<JBPMNode> public class JBPMNodeList extends ArrayList<JBPMNode>
{ {
private static final long serialVersionUID = 1376915749912156471L; private static final long serialVersionUID = 1376915749912156471L;
protected ServiceRegistry services;
/**
* Construct
*
* @param nodeRef node reference
* @param services services
*/
public JBPMNodeList(ServiceRegistry services)
{
super();
this.services = services;
}
/**
* Accessor to retrieve a named property on all nodes in the list
*
* @param propertyName the name of the property to retrieve
* @return a collection of property name values
*/
public Map<String, Collection<Object>> getValues(String propertyName)
{
ValuesMap<String, Collection<Object>> values = new ValuesMap<String, Collection<Object>>(null);
for (int i = 0; i < JBPMNodeList.this.size(); i++)
{
JBPMNode node = JBPMNodeList.this.get(i);
Map<String, Object> nodeValues = node.getProperties();
for (String key : nodeValues.keySet())
{
values.put(key, null);
}
}
return values;
}
public class ValuesMap<K, V> extends QNameMap<K, V>
{
/**
*
*/
private static final long serialVersionUID = -6463958742416258009L;
public ValuesMap(NamespacePrefixResolver resolver)
{
super(resolver);
// TODO Auto-generated constructor stub
}
@Override
public Object get(Object key)
{
Collection<Object> values = null;
if (containsKey(key))
{
values = new ArrayList<Object>(this.size());
for (int i = 0; i < JBPMNodeList.this.size(); i++)
{
JBPMNode node = JBPMNodeList.this.get(i);
Object value = node.getProperties().get(key);
values.add(value);
}
}
return values;
}
}
} }

View File

@ -83,7 +83,7 @@ public class NodeListConverter extends SerializableToByteArrayConverter
BeanFactoryReference factory = jbpmFactoryLocator.useBeanFactory(null); BeanFactoryReference factory = jbpmFactoryLocator.useBeanFactory(null);
ServiceRegistry serviceRegistry = (ServiceRegistry)factory.getFactory().getBean(ServiceRegistry.SERVICE_REGISTRY); ServiceRegistry serviceRegistry = (ServiceRegistry)factory.getFactory().getBean(ServiceRegistry.SERVICE_REGISTRY);
JBPMNodeList nodes = new JBPMNodeList(); JBPMNodeList nodes = new JBPMNodeList(serviceRegistry);
for (NodeRef nodeRef : nodeRefs) for (NodeRef nodeRef : nodeRefs)
{ {
nodes.add(new JBPMNode(nodeRef, serviceRegistry)); nodes.add(new JBPMNode(nodeRef, serviceRegistry));

View File

@ -1,70 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="wf:adhoc">
<swimlane name="initiator"/>
<start-state name="start">
<task name="wf:submitAdhocTask" swimlane="initiator">
<controller>
<variable name="assignee" access="write" mapped-name="wf:assignee"/>
<variable name="adhocduedate" access="write" mapped-name="wf:adhocDueDate"/>
<variable name="adhocpriority" access="write" mapped-name="wf:adhocPriority"/>
<variable name="adhocdescription" access="write" mapped-name="wf:adhocDescription"/>
<variable name="notify" access="write" mapped-name="wf:notifyMe"/>
<variable name="workflowpackage" access="write" mapped-name="bpm:package"/>
<variable name="workflowcontext" access="write" mapped-name="bpm:context"/>
</controller>
</task>
<transition name="" to="adhoc"/>
</start-state>
<swimlane name="assignee">
<assignment actor-id="#{assignee.properties['cm:userName']}"/>
</swimlane>
<task-node name="adhoc">
<event type="task-create">
<script>
taskInstance.dueDate = adhocduedate;
taskInstance.priority = adhocpriority;
</script>
</event>
<task name="wf:adhocTask" swimlane="assignee">
<controller>
<variable name="adhocdescription" access="read" mapped-name="wf:adhocDescription"/>
<variable name="workflowpackage" access="read" mapped-name="bpm:package"/>
<variable name="workflowcontext" access="read" mapped-name="bpm:context"/>
</controller>
</task>
<transition name="" to="completed">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
if (notify)
{
var mail = actions.create("mail");
mail.parameters.to = initiator.properties["cm:email"];
mail.parameters.subject = "Adhoc Task " + adhocdescription;
mail.parameters.from = assignee.properties["cm:email"];
mail.parameters.text = "It's done";
mail.execute(workflowpackage);
}
</script>
</action>
</transition>
</task-node>
<task-node name="completed">
<task name="wf:completedAdhocTask" swimlane="initiator">
<controller>
<variable name="adhocdescription" access="read" mapped-name="wf:adhocDescription"/>
<variable name="workflowpackage" access="read" mapped-name="bpm:package"/>
<variable name="workflowcontext" access="read" mapped-name="bpm:context"/>
</controller>
</task>
<transition name="" to="end"/>
</task-node>
<end-state name="end"/>
</process-definition>

View File

@ -1,63 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="wf:review">
<swimlane name="initiator"/>
<start-state name="start">
<task name="wf:submitReviewTask" swimlane="initiator">
<controller>
<variable name="reviewer" access="write" mapped-name="wf:reviewer"/>
<variable name="reviewduedate" access="write" mapped-name="wf:reviewDueDate"/>
<variable name="reviewpriority" access="write" mapped-name="wf:reviewPriority"/>
<variable name="package" access="write" mapped-name="bpm:package"/>
<variable name="context" access="write" mapped-name="bpm:context"/>
</controller>
</task>
<transition name="" to="review"/>
</start-state>
<swimlane name="reviewer">
<assignment actor-id="#{reviewer.properties['cm:userName']}"/>
</swimlane>
<task-node name="review">
<task name="wf:reviewTask" swimlane="reviewer">
<event type="task-create">
<script>
taskInstance.dueDate = reviewduedate;
taskInstance.priority = reviewpriority;
</script>
</event>
<controller>
<variable name="package" access="read,required" mapped-name="bpm:package"/>
<variable name="context" access="read,required" mapped-name="bpm:context"/>
</controller>
</task>
<transition name="reject" to="rejected"/>
<transition name="approve" to="approved"/>
</task-node>
<task-node name="rejected">
<task name="wf:rejectedTask" swimlane="initiator">
<controller>
<variable name="package" access="read,required" mapped-name="bpm:package"/>
<variable name="context" access="read,required" mapped-name="bpm:context"/>
</controller>
</task>
<transition name="" to="end"/>
</task-node>
<task-node name="approved">
<task name="wf:approvedTask" swimlane="initiator">
<controller>
<variable name="package" access="read,required" mapped-name="bpm:package"/>
<variable name="context" access="read,required" mapped-name="bpm:context"/>
</controller>
</task>
<transition name="" to="end"/>
</task-node>
<end-state name="end"/>
</process-definition>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="wf:pooledtasks">
<swimlane name="initiator" />
<start-state name="start">
<task name="wf:submitParallelReviewTask" swimlane="initiator" />
<transition name="" to="review" />
</start-state>
<swimlane name="reviewer">
<assignment class="org.alfresco.repo.workflow.jbpm.AlfrescoAssignment">
<pooledactors>#{bpm_assignees}</pooledactors>
</assignment>
</swimlane>
<task-node name="review">
<task name="wf:reviewTask" swimlane="reviewer"/>
<transition name="" to="end" />
</task-node>
<end-state name="end" />
</process-definition>