Workflow (server-side) groups support

- assignment of groups to pooled tasks
- retrieve group members added to javascript

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4805 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
David Caruana
2007-01-12 12:20:00 +00:00
parent 26faea4e62
commit 7dccfe18fb
15 changed files with 486 additions and 48 deletions

View File

@@ -17,14 +17,20 @@
package org.alfresco.repo.workflow.jbpm;
import java.util.Collection;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.jscript.Node;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
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;
import org.springframework.beans.factory.BeanFactory;
/**
@@ -33,13 +39,24 @@ import org.jbpm.taskmgmt.exe.Assignable;
*
* @author davidc
*/
public class AlfrescoAssignment implements AssignmentHandler
public class AlfrescoAssignment extends JBPMSpringAssignmentHandler
{
private static final long serialVersionUID = 1025667849552265719L;
private ServiceRegistry services;
private Element actor;
private Element pooledactors;
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler#initialiseHandler(org.springframework.beans.factory.BeanFactory)
*/
@Override
protected void initialiseHandler(BeanFactory factory)
{
services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
}
/* (non-Javadoc)
* @see org.jbpm.taskmgmt.def.AssignmentHandler#assign(org.jbpm.taskmgmt.exe.Assignable, org.jbpm.graph.exe.ExecutionContext)
@@ -63,7 +80,8 @@ public class AlfrescoAssignment implements AssignmentHandler
{
if (actorValStr.startsWith("#{"))
{
Object eval = JbpmExpressionEvaluator.evaluate(actorValStr, executionContext);
String expression = actorValStr.substring(2, actorValStr.length() -1);
Object eval = AlfrescoJavaScript.executeScript(executionContext, services, expression, null);
if (eval == null)
{
throw new WorkflowException("actor expression '" + actorValStr + "' evaluates to null");
@@ -73,9 +91,9 @@ public class AlfrescoAssignment implements AssignmentHandler
{
assignedActor = (String)eval;
}
else if (eval instanceof JBPMNode)
else if (eval instanceof Node)
{
JBPMNode node = (JBPMNode)eval;
Node node = (Node)eval;
if (!node.getType().equals(ContentModel.TYPE_PERSON))
{
throw new WorkflowException("actor expression does not evaluate to a person");
@@ -106,49 +124,52 @@ public class AlfrescoAssignment implements AssignmentHandler
{
if (pooledactorValStr.startsWith("#{"))
{
Object eval = JbpmExpressionEvaluator.evaluate(pooledactorValStr, executionContext);
String expression = pooledactorValStr.substring(2, pooledactorValStr.length() -1);
Object eval = AlfrescoJavaScript.executeScript(executionContext, services, expression, null);
if (eval == null)
{
throw new WorkflowException("pooledactors expression '" + pooledactorValStr + "' evaluates to null");
}
if (eval instanceof Collection)
if (eval instanceof Node[])
{
Collection coll = (Collection)eval;
assignedPooledActors = new String[coll.size()];
Node[] nodes = (Node[])eval;
assignedPooledActors = new String[nodes.length];
int i = 0;
for (Object obj : coll)
for (Node node : (Node[])nodes)
{
if (!(obj instanceof JBPMNode))
if (node.getType().equals(ContentModel.TYPE_PERSON))
{
throw new WorkflowException("pooledactors does not refer to a collection of people");
assignedPooledActors[i++] = (String)node.getProperties().get(ContentModel.PROP_USERNAME);
}
JBPMNode node = (JBPMNode)obj;
if (!node.getType().equals(ContentModel.TYPE_PERSON))
else if (node.getType().equals(ContentModel.TYPE_AUTHORITY_CONTAINER))
{
throw new WorkflowException("pooledactors expression does not evaluate to a collection of people");
assignedPooledActors[i++] = (String)node.getProperties().get(ContentModel.PROP_AUTHORITY_NAME);
}
else
{
throw new WorkflowException("pooledactors expression does not evaluate to a collection of authorities");
}
assignedPooledActors[i++] = (String)node.getProperties().get(ContentModel.PROP_USERNAME);
}
}
else if (eval instanceof JBPMNode)
else if (eval instanceof Node)
{
JBPMNode node = (JBPMNode)eval;
assignedPooledActors = new String[1];
Node node = (Node)eval;
if (node.getType().equals(ContentModel.TYPE_PERSON))
{
assignedPooledActors[0] = (String)node.getProperties().get(ContentModel.PROP_USERNAME);
}
// TODO: Support Group
else if (node.getType().equals(ContentModel.TYPE_AUTHORITY_CONTAINER))
{
assignedPooledActors[0] = (String)node.getProperties().get(ContentModel.PROP_AUTHORITY_NAME);
}
else
{
throw new WorkflowException("pooledactors expression does not evaluate to a collection of people");
throw new WorkflowException("pooledactors expression does not evaluate to a collection of authorities");
}
}
else
{
throw new WorkflowException("pooledactor expression does not evaluate to a group or collection of people");
}
}
else
{

View File

@@ -56,7 +56,6 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
private static final long serialVersionUID = -2908748080671212745L;
private static JpdlXmlReader jpdlReader = new JpdlXmlReader((InputSource)null);
private ScriptService scriptService;
private ServiceRegistry services;
private Element script;
@@ -67,7 +66,6 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
@Override
protected void initialiseHandler(BeanFactory factory)
{
scriptService = (ScriptService)factory.getBean(ServiceRegistry.SCRIPT_SERVICE.getLocalName());
services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
}
@@ -116,9 +114,8 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
expression = expressionElement.getTextTrim();
}
// construct script arguments and execute
Map<String, Object> inputMap = createInputMap(executionContext, variableAccesses);
Object result = scriptService.executeScriptString(expression, inputMap);
// execute
Object result = executeScript(executionContext, services, expression, variableAccesses);
// map script return variable to process context
VariableAccess returnVariable = getWritableVariable(variableAccesses);
@@ -130,6 +127,24 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
}
}
/**
* Execute a script
*
* @param context jBPM execution context
* @param services Alfresco service registry
* @param expression script to execute
* @param variableAccesses (optional) list of jBPM variables to map into script (all, if not supplied)
* @return script result
*/
public static Object executeScript(ExecutionContext context, ServiceRegistry services, String expression, List<VariableAccess> variableAccesses)
{
Map<String, Object> inputMap = createInputMap(context, services, variableAccesses);
ScriptService scriptService = services.getScriptService();
Object result = scriptService.executeScriptString(expression, inputMap);
return result;
}
/**
* Construct map of arguments to pass to script
@@ -141,7 +156,7 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
* @return the map of script arguments
*/
@SuppressWarnings("unchecked")
public Map<String, Object> createInputMap(ExecutionContext executionContext, List<VariableAccess> variableAccesses)
private static Map<String, Object> createInputMap(ExecutionContext executionContext, ServiceRegistry services, List<VariableAccess> variableAccesses)
{
Map<String, Object> inputMap = new HashMap<String, Object>();
@@ -216,7 +231,7 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
* @param variableAccesses the variables configuration
* @return true => there are variables to read
*/
private boolean hasReadableVariable(List<VariableAccess> variableAccesses)
private static boolean hasReadableVariable(List<VariableAccess> variableAccesses)
{
if (variableAccesses != null)
{
@@ -238,7 +253,7 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
* @param variableAccesses the variables configuration
* @return true => there is a variable to write
*/
private VariableAccess getWritableVariable(List<VariableAccess> variableAccesses)
private static VariableAccess getWritableVariable(List<VariableAccess> variableAccesses)
{
if (variableAccesses != null)
{

View File

@@ -20,27 +20,37 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.workflow.WorkflowException;
import org.dom4j.Element;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.def.Node;
import org.jbpm.graph.def.Transition;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.exe.Token;
import org.jbpm.instantiation.FieldInstantiator;
import org.jbpm.jpdl.el.impl.JbpmExpressionEvaluator;
import org.springframework.beans.factory.BeanFactory;
/**
* For each "item in collection", create a fork.
*/
public class ForEachFork implements ActionHandler
public class ForEachFork extends JBPMSpringActionHandler
{
private static final long serialVersionUID = 4643103713602441652L;
private ServiceRegistry services;
private Element foreach;
private String var;
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler#initialiseHandler(org.springframework.beans.factory.BeanFactory)
*/
@Override
protected void initialiseHandler(BeanFactory factory)
{
services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
}
/**
* Create a new child token for each item in list.
@@ -68,7 +78,8 @@ public class ForEachFork implements ActionHandler
{
if (forEachCollStr.startsWith("#{"))
{
Object eval = JbpmExpressionEvaluator.evaluate(forEachCollStr, executionContext);
String expression = forEachCollStr.substring(2, forEachCollStr.length() -1);
Object eval = AlfrescoJavaScript.executeScript(executionContext, services, expression, null);
if (eval == null)
{
throw new WorkflowException("forEach expression '" + forEachCollStr + "' evaluates to null");
@@ -85,11 +96,23 @@ public class ForEachFork implements ActionHandler
}
}
// expression evaluates to Node array
else if (eval instanceof org.alfresco.repo.jscript.Node[])
{
org.alfresco.repo.jscript.Node[] nodes = (org.alfresco.repo.jscript.Node[])eval;
forEachColl = new ArrayList(nodes.length);
for (org.alfresco.repo.jscript.Node node : nodes)
{
forEachColl.add(new JBPMNode(node.getNodeRef(), services));
}
}
// expression evaluates to collection
else if (eval instanceof Collection)
{
forEachColl = (List)eval;
}
}
}
else

View File

@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -33,6 +34,7 @@ import org.alfresco.i18n.I18NUtil;
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.BPMEngine;
import org.alfresco.repo.workflow.TaskComponent;
import org.alfresco.repo.workflow.WorkflowComponent;
@@ -49,6 +51,8 @@ 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.security.AuthorityService;
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;
@@ -108,6 +112,8 @@ public class JBPMEngine extends BPMEngine
protected NodeService nodeService;
protected ServiceRegistry serviceRegistry;
protected PersonService personService;
protected AuthorityService authorityService;
protected AuthorityDAO authorityDAO;
protected JbpmTemplate jbpmTemplate;
// Company Home
@@ -183,6 +189,26 @@ public class JBPMEngine extends BPMEngine
this.personService = personService;
}
/**
* Sets the Authority Service
*
* @param authorityService
*/
public void setAuthorityService(AuthorityService authorityService)
{
this.authorityService = authorityService;
}
/**
* Sets the Authority DAO
*
* @param authorityDAO
*/
public void setAuthorityDAO(AuthorityDAO authorityDAO)
{
this.authorityDAO = authorityDAO;
}
/**
* Sets the Service Registry
*
@@ -459,7 +485,7 @@ public class JBPMEngine extends BPMEngine
processContext.setVariable("cancelled", false);
NodeRef companyHome = getCompanyHome();
processContext.setVariable("companyhome", new JBPMNode(companyHome, serviceRegistry));
NodeRef initiatorPerson = mapNameToAuthority(currentUserName);
NodeRef initiatorPerson = mapNameToPerson(currentUserName);
if (initiatorPerson != null)
{
processContext.setVariable("initiator", new JBPMNode(initiatorPerson, serviceRegistry));
@@ -831,9 +857,17 @@ public class JBPMEngine extends BPMEngine
{
public List<WorkflowTask> doInJbpm(JbpmContext context)
{
// retrieve pooled tasks for specified authorities
// flatten authorities to include all parent authorities
Set<String> flattenedAuthorities = new HashSet<String>();
for (String authority : authorities)
{
Set<String> parents = authorityService.getContainingAuthorities(AuthorityType.GROUP, authority, false);
flattenedAuthorities.addAll(parents);
}
// retrieve pooled tasks for all flattened authorities
TaskMgmtSession taskSession = context.getTaskMgmtSession();
List<TaskInstance> tasks = taskSession.findPooledTaskInstances(authorities);
List<TaskInstance> tasks = taskSession.findPooledTaskInstances(new ArrayList(flattenedAuthorities));
List<WorkflowTask> workflowTasks = new ArrayList<WorkflowTask>(tasks.size());
for (TaskInstance task : tasks)
{
@@ -1281,7 +1315,7 @@ public class JBPMEngine extends BPMEngine
}
/**
* Sets Properties of Task
* Gets Properties of Task
*
* @param instance task instance
* @param properties properties to set
@@ -1778,12 +1812,12 @@ public class JBPMEngine extends BPMEngine
}
/**
* Convert authority name to an Alfresco Authority
* Convert person name to an Alfresco Person
*
* @param names the authority names to convert
* @return the Alfresco authorities
* @param names the person name to convert
* @return the Alfresco person
*/
private NodeRef mapNameToAuthority(String name)
private NodeRef mapNameToPerson(String name)
{
NodeRef authority = null;
if (name != null)
@@ -1797,6 +1831,26 @@ public class JBPMEngine extends BPMEngine
return authority;
}
/**
* Convert authority name to an Alfresco Authority
*
* @param names 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;
}
/**
* Map jBPM variable name to QName
*

View File

@@ -0,0 +1,54 @@
/*
* 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 org.jbpm.taskmgmt.def.AssignmentHandler;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
/**
* Abstract base implementation of a Jbpm Assignment Handler with access to
* Alfresco Spring beans.
*
* @author davidc
*/
public abstract class JBPMSpringAssignmentHandler implements AssignmentHandler
{
/**
* Construct
*/
protected JBPMSpringAssignmentHandler()
{
// The following implementation is derived from Spring Modules v0.4
BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
BeanFactoryReference factory = factoryLocator.useBeanFactory(null);
initialiseHandler(factory.getFactory());
}
/**
* Initialise Action Handler
*
* @param factory Spring bean factory for accessing Alfresco beans
*/
protected abstract void initialiseHandler(BeanFactory factory);
}

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="wf:grouptasks">
<swimlane name="initiator" />
<start-state name="start">
<task name="wf:submitGroupReviewTask" swimlane="initiator" />
<transition name="" to="review" />
<transition name="parallel" to="parallel" />
</start-state>
<swimlane name="reviewer">
<assignment class="org.alfresco.repo.workflow.jbpm.AlfrescoAssignment">
<pooledactors>#{bpm_groupAssignee}</pooledactors>
</assignment>
</swimlane>
<task-node name="review">
<task name="wf:reviewTask" swimlane="reviewer"/>
<transition name="" to="end" />
</task-node>
<node name="parallel">
<action class="org.alfresco.repo.workflow.jbpm.ForEachFork">
<foreach>#{people.getMembers(bpm_groupAssignee)}</foreach>
<var>reviewer</var>
</action>
<transition name="review" to="parallelreview" />
</node>
<task-node name="parallelreview">
<task name="wf:reviewTask">
<assignment class="org.alfresco.repo.workflow.jbpm.AlfrescoAssignment">
<actor>#{reviewer}</actor>
</assignment>
</task>
<transition name="" to="endparallel" />
</task-node>
<join name="endparallel">
<transition to="end" />
</join>
<end-state name="end" />
</process-definition>