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

@@ -549,17 +549,22 @@
<bean id="dictionaryBootstrap" parent="dictionaryModelBootstrap" depends-on="resourceBundles"> <bean id="dictionaryBootstrap" parent="dictionaryModelBootstrap" depends-on="resourceBundles">
<property name="models"> <property name="models">
<list> <list>
<!-- System models -->
<value>alfresco/model/dictionaryModel.xml</value> <value>alfresco/model/dictionaryModel.xml</value>
<value>alfresco/model/systemModel.xml</value> <value>alfresco/model/systemModel.xml</value>
<value>org/alfresco/repo/security/authentication/userModel.xml</value>
<!-- Content models -->
<value>alfresco/model/contentModel.xml</value> <value>alfresco/model/contentModel.xml</value>
<value>alfresco/model/bpmModel.xml</value> <value>alfresco/model/bpmModel.xml</value>
<value>alfresco/model/wcmModel.xml</value> <value>alfresco/model/wcmModel.xml</value>
<value>alfresco/model/applicationModel.xml</value>
<value>alfresco/model/wcmAppModel.xml</value>
<value>alfresco/model/forumModel.xml</value> <value>alfresco/model/forumModel.xml</value>
<!-- Content models -->
<value>alfresco/model/applicationModel.xml</value>
<value>alfresco/model/wcmAppModel.xml</value>
<!-- Implementation models --> <!-- Implementation models -->
<value>org/alfresco/repo/security/authentication/userModel.xml</value>
<value>org/alfresco/repo/action/actionModel.xml</value> <value>org/alfresco/repo/action/actionModel.xml</value>
<value>org/alfresco/repo/rule/ruleModel.xml</value> <value>org/alfresco/repo/rule/ruleModel.xml</value>
<value>org/alfresco/repo/version/version_model.xml</value> <value>org/alfresco/repo/version/version_model.xml</value>

View File

@@ -91,6 +91,18 @@ ok> var <varName>[*] person <varValue>
var bpm:assignee* person admin,fred var bpm:assignee* person admin,fred
ok> var <varName>[*] group <varValue>
Define or update a (usr:authorityContainer) node ref variable.
<varName> variable name
[*] if specified, define a collection
<varValue> variable value (comma-seperate to specify a list of values)
e.g.
var bpm:groupAssignee group GROUP_Engineering
ok> var <varName> package <itemCount> ok> var <varName> package <itemCount>
Define or update a (bpm:workflowPackage) node ref variable. Define or update a (bpm:workflowPackage) node ref variable.

View File

@@ -12,6 +12,8 @@
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d" /> <import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d" />
<!-- Import Alfresco Content Domain Model Definitions --> <!-- Import Alfresco Content Domain Model Definitions -->
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm" /> <import uri="http://www.alfresco.org/model/content/1.0" prefix="cm" />
<!-- Import User Model Definitions -->
<import uri="http://www.alfresco.org/model/user/1.0" prefix="usr"/>
</imports> </imports>
<namespaces> <namespaces>
@@ -310,6 +312,29 @@
</associations> </associations>
</aspect> </aspect>
<!-- -->
<!-- Single Group Assignee -->
<!-- -->
<aspect name="bpm:groupAssignee">
<associations>
<association name="bpm:groupAssignee">
<source>
<mandatory>false</mandatory>
<many>false</many>
</source>
<target>
<!-- TODO: Refactor when group is pulled into content model -->
<class>usr:authorityContainer</class>
<mandatory>true</mandatory>
<many>false</many>
</target>
</association>
</associations>
</aspect>
<!-- --> <!-- -->
<!-- A collection of content routed through a workflow. --> <!-- A collection of content routed through a workflow. -->
<!-- --> <!-- -->

View File

@@ -70,6 +70,9 @@
<property name="serviceRegistry"> <property name="serviceRegistry">
<ref bean="ServiceRegistry"/> <ref bean="ServiceRegistry"/>
</property> </property>
<property name="authorityDAO">
<ref bean="authorityDAO"/>
</property>
</bean> </bean>
<bean id="sessionScript" parent="baseScriptImplementation" class="org.alfresco.repo.jscript.Session"> <bean id="sessionScript" parent="baseScriptImplementation" class="org.alfresco.repo.jscript.Session">

View File

@@ -34,6 +34,8 @@
<property name="nodeService" ref="NodeService"/> <property name="nodeService" ref="NodeService"/>
<property name="namespaceService" ref="namespaceService"/> <property name="namespaceService" ref="namespaceService"/>
<property name="personService" ref="PersonService"/> <property name="personService" ref="PersonService"/>
<property name="authorityDAO" ref="authorityDAO"/>
<property name="transactionService" ref="transactionComponent"/>
<property name="fileFolderService" ref="FileFolderService"/> <property name="fileFolderService" ref="FileFolderService"/>
</bean> </bean>
@@ -87,6 +89,8 @@
<property name="namespaceService" ref="namespaceService"/> <property name="namespaceService" ref="namespaceService"/>
<property name="nodeService" ref="nodeService"/> <property name="nodeService" ref="nodeService"/>
<property name="personService" ref="personService"/> <property name="personService" ref="personService"/>
<property name="authorityService" ref="authorityService"/>
<property name="authorityDAO" ref="authorityDAO"/>
<property name="serviceRegistry" ref="ServiceRegistry"/> <property name="serviceRegistry" ref="ServiceRegistry"/>
<property name="companyHomeStore"><value>${spaces.store}</value></property> <property name="companyHomeStore"><value>${spaces.store}</value></property>
<property name="companyHomePath"><value>/${spaces.company_home.childname}</value></property> <property name="companyHomePath"><value>/${spaces.company_home.childname}</value></property>

View File

@@ -56,7 +56,7 @@
<task-node name="serialreview"> <task-node name="serialreview">
<task name="wcmwf:reviewTask"> <task name="wcmwf:reviewTask">
<assignment class="org.alfresco.repo.workflow.jbpm.AlfrescoAssignment"> <assignment class="org.alfresco.repo.workflow.jbpm.AlfrescoAssignment">
<actor>#{bpm_assignees[wcmwf_approveCnt]}</actor> <actor>#{bpm_assignees.get(wcmwf_approveCnt)}</actor>
</assignment> </assignment>
</task> </task>

View File

@@ -24,6 +24,13 @@
</mandatory-aspects> </mandatory-aspects>
</type> </type>
<type name="wf:submitGroupReviewTask">
<parent>bpm:startTask</parent>
<mandatory-aspects>
<aspect>bpm:groupAssignee</aspect>
</mandatory-aspects>
</type>
<type name="wf:submitParallelReviewTask"> <type name="wf:submitParallelReviewTask">
<parent>bpm:startTask</parent> <parent>bpm:startTask</parent>
<properties> <properties>

View File

@@ -16,8 +16,14 @@
*/ */
package org.alfresco.repo.jscript; package org.alfresco.repo.jscript;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authority.AuthorityDAO;
import org.alfresco.service.ServiceRegistry; import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
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.security.PersonService;
import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Scriptable;
@@ -30,6 +36,7 @@ public final class People extends BaseScriptImplementation implements Scopeable
{ {
/** Repository Service Registry */ /** Repository Service Registry */
private ServiceRegistry services; private ServiceRegistry services;
private AuthorityDAO authorityDAO;
/** Root scope for this object */ /** Root scope for this object */
private Scriptable scope; private Scriptable scope;
@@ -44,6 +51,16 @@ public final class People extends BaseScriptImplementation implements Scopeable
this.services = serviceRegistry; this.services = serviceRegistry;
} }
/**
* Set the authority DAO
*
* @param authorityDAO authority dao
*/
public void setAuthorityDAO(AuthorityDAO authorityDAO)
{
this.authorityDAO = authorityDAO;
}
/** /**
* @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable) * @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable)
*/ */
@@ -55,6 +72,7 @@ public final class People extends BaseScriptImplementation implements Scopeable
/** /**
* Gets the Person given the username * Gets the Person given the username
* *
* @param username the username of the person to get
* @return the person node (type cm:person) or null if no such person exists * @return the person node (type cm:person) or null if no such person exists
*/ */
public Node getPerson(String username) public Node getPerson(String username)
@@ -69,4 +87,87 @@ public final class People extends BaseScriptImplementation implements Scopeable
return person; return person;
} }
/**
* Gets the Group given the group name
*
* @param groupName name of group to get
* @return the group node (type usr:authorityContainer) or null if no such group exists
*/
public Node getGroup(String groupName)
{
Node group = null;
NodeRef groupRef = authorityDAO.getAuthorityNodeRefOrNull(groupName);
if (groupRef != null)
{
group = new Node(groupRef, services, scope);
}
return group;
}
/**
* Gets the members (people) of a group (including all sub-groups)
*
* @param group the group to retrieve members for
* @param recurse recurse into sub-groups
* @return the members of the group
*/
public Node[] getMembers(Node group)
{
return getContainedAuthorities(group, AuthorityType.USER, true);
}
/**
* Gets the members (people) of a group
*
* @param group the group to retrieve members for
* @param recurse recurse into sub-groups
* @return the members of the group
*/
public Node[] getMembers(Node group, boolean recurse)
{
return getContainedAuthorities(group, AuthorityType.USER, recurse);
}
/**
* Get Contained Authorities
*
* @param container authority containers
* @param type authority type to filter by
* @param recurse recurse into sub-containers
* @return contained authorities
*/
private Node[] getContainedAuthorities(Node container, AuthorityType type, boolean recurse)
{
Node[] members = null;
if (container.getType().equals(ContentModel.TYPE_AUTHORITY_CONTAINER))
{
AuthorityService authorityService = services.getAuthorityService();
String groupName = (String)container.getProperties().get(ContentModel.PROP_AUTHORITY_NAME);
Set<String> authorities = authorityService.getContainedAuthorities(type, groupName, !recurse);
members = new Node[authorities.size()];
int i = 0;
for (String authority : authorities)
{
AuthorityType authorityType = AuthorityType.getAuthorityType(authority);
if (authorityType.equals(AuthorityType.GROUP))
{
Node group = getGroup(authority);
if (group != null)
{
members[i++] = group;
}
}
else if (authorityType.equals(AuthorityType.USER))
{
Node person = getPerson(authority);
if (person != null)
{
members[i++] = person;
}
}
}
}
return members;
}
} }

View File

@@ -38,6 +38,8 @@ import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.avmsync.AVMDifference; import org.alfresco.service.cmr.avmsync.AVMDifference;
import org.alfresco.service.cmr.avmsync.AVMSyncService; import org.alfresco.service.cmr.avmsync.AVMSyncService;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.authority.AuthorityDAO;
import org.alfresco.repo.transaction.TransactionUtil;
import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.ContentWriter;
@@ -46,6 +48,7 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowDefinition; import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowDeployment; 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.WorkflowInstance;
import org.alfresco.service.cmr.workflow.WorkflowPath; import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowService; import org.alfresco.service.cmr.workflow.WorkflowService;
@@ -54,6 +57,7 @@ import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.cmr.workflow.WorkflowTransition; import org.alfresco.service.cmr.workflow.WorkflowTransition;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.GUID; import org.alfresco.util.GUID;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@@ -70,6 +74,8 @@ public class WorkflowInterpreter
private WorkflowService workflowService; private WorkflowService workflowService;
private NamespaceService namespaceService; private NamespaceService namespaceService;
private NodeService nodeService; private NodeService nodeService;
private TransactionService transactionService;
private AuthorityDAO authorityDAO;
private AVMService avmService; private AVMService avmService;
private AVMSyncService avmSyncService; private AVMSyncService avmSyncService;
private PersonService personService; private PersonService personService;
@@ -167,6 +173,22 @@ public class WorkflowInterpreter
this.personService = personService; this.personService = personService;
} }
/**
* @param transactionService transactionService
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* @param authorityDAO authorityDAO
*/
public void setAuthorityDAO(AuthorityDAO authorityDAO)
{
this.authorityDAO = authorityDAO;
}
/** /**
* @param fileFolderService fileFolderService * @param fileFolderService fileFolderService
*/ */
@@ -217,11 +239,17 @@ public class WorkflowInterpreter
{ {
// execute command in context of currently selected user // execute command in context of currently selected user
return AuthenticationUtil.runAs(new RunAsWork<String>() return AuthenticationUtil.runAs(new RunAsWork<String>()
{
public String doWork() throws Exception
{
return TransactionUtil.executeInUserTransaction(transactionService, new TransactionUtil.TransactionWork<String>()
{ {
public String doWork() throws Exception public String doWork() throws Exception
{ {
return executeCommand(line); return executeCommand(line);
} }
});
}
}, username); }, username);
} }
@@ -882,6 +910,45 @@ public class WorkflowInterpreter
} }
out.println("set var " + qname + " = " + vars.get(qname)); out.println("set var " + qname + " = " + vars.get(qname));
} }
else if (command[2].equals("group"))
{
boolean multi = false;
if (command[1].endsWith("*"))
{
command[1] = command[1].substring(0, command[1].length() -1);
multi = true;
}
QName qname = QName.createQName(command[1], namespaceService);
String[] strValues = command[3].split(",");
if (!multi && strValues.length > 1)
{
return "Syntax Error.\n";
}
if (!multi)
{
NodeRef auth = authorityDAO.getAuthorityNodeRefOrNull(strValues[0]);
if (auth == null)
{
throw new WorkflowException("Group " + strValues[0] + " does not exist.");
}
vars.put(qname, auth);
}
else
{
List<NodeRef> values = new ArrayList<NodeRef>();
for (String strValue : strValues)
{
NodeRef auth = authorityDAO.getAuthorityNodeRefOrNull(strValue);
if (auth == null)
{
throw new WorkflowException("Group " + strValue + " does not exist.");
}
values.add(auth);
}
vars.put(qname, (Serializable)values);
}
out.println("set var " + qname + " = " + vars.get(qname));
}
else if (command[2].equals("avmpackage")) else if (command[2].equals("avmpackage"))
{ {
// lookup source folder of changes // lookup source folder of changes

View File

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

View File

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

View File

@@ -20,28 +20,38 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.workflow.WorkflowException; import org.alfresco.service.cmr.workflow.WorkflowException;
import org.dom4j.Element; import org.dom4j.Element;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.def.Node; import org.jbpm.graph.def.Node;
import org.jbpm.graph.def.Transition; import org.jbpm.graph.def.Transition;
import org.jbpm.graph.exe.ExecutionContext; import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.exe.Token; import org.jbpm.graph.exe.Token;
import org.jbpm.instantiation.FieldInstantiator; 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. * 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 static final long serialVersionUID = 4643103713602441652L;
private ServiceRegistry services;
private Element foreach; private Element foreach;
private String var; 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. * Create a new child token for each item in list.
* *
@@ -68,7 +78,8 @@ public class ForEachFork implements ActionHandler
{ {
if (forEachCollStr.startsWith("#{")) 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) if (eval == null)
{ {
throw new WorkflowException("forEach expression '" + forEachCollStr + "' evaluates to 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 // expression evaluates to collection
else if (eval instanceof Collection) else if (eval instanceof Collection)
{ {
forEachColl = (List)eval; forEachColl = (List)eval;
} }
} }
} }
else else

View File

@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -33,6 +34,7 @@ import org.alfresco.i18n.I18NUtil;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil; 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.BPMEngine;
import org.alfresco.repo.workflow.TaskComponent; import org.alfresco.repo.workflow.TaskComponent;
import org.alfresco.repo.workflow.WorkflowComponent; 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.NodeService;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; 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.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowDefinition; import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowDeployment; import org.alfresco.service.cmr.workflow.WorkflowDeployment;
@@ -108,6 +112,8 @@ public class JBPMEngine extends BPMEngine
protected NodeService nodeService; protected NodeService nodeService;
protected ServiceRegistry serviceRegistry; protected ServiceRegistry serviceRegistry;
protected PersonService personService; protected PersonService personService;
protected AuthorityService authorityService;
protected AuthorityDAO authorityDAO;
protected JbpmTemplate jbpmTemplate; protected JbpmTemplate jbpmTemplate;
// Company Home // Company Home
@@ -183,6 +189,26 @@ public class JBPMEngine extends BPMEngine
this.personService = personService; 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 * Sets the Service Registry
* *
@@ -459,7 +485,7 @@ public class JBPMEngine extends BPMEngine
processContext.setVariable("cancelled", false); processContext.setVariable("cancelled", false);
NodeRef companyHome = getCompanyHome(); NodeRef companyHome = getCompanyHome();
processContext.setVariable("companyhome", new JBPMNode(companyHome, serviceRegistry)); processContext.setVariable("companyhome", new JBPMNode(companyHome, serviceRegistry));
NodeRef initiatorPerson = mapNameToAuthority(currentUserName); NodeRef initiatorPerson = mapNameToPerson(currentUserName);
if (initiatorPerson != null) if (initiatorPerson != null)
{ {
processContext.setVariable("initiator", new JBPMNode(initiatorPerson, serviceRegistry)); processContext.setVariable("initiator", new JBPMNode(initiatorPerson, serviceRegistry));
@@ -831,9 +857,17 @@ public class JBPMEngine extends BPMEngine
{ {
public List<WorkflowTask> doInJbpm(JbpmContext context) 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(); 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()); List<WorkflowTask> workflowTasks = new ArrayList<WorkflowTask>(tasks.size());
for (TaskInstance task : tasks) 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 instance task instance
* @param properties properties to set * @param properties properties to set
@@ -1777,6 +1811,26 @@ public class JBPMEngine extends BPMEngine
return value; return value;
} }
/**
* Convert person name to an Alfresco Person
*
* @param names 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 * Convert authority name to an Alfresco Authority
* *
@@ -1789,9 +1843,9 @@ public class JBPMEngine extends BPMEngine
if (name != null) if (name != null)
{ {
// TODO: Should this be an exception? // TODO: Should this be an exception?
if (personService.personExists(name)) if (authorityDAO.authorityExists(name))
{ {
authority = personService.getPerson(name); authority = authorityDAO.getAuthorityNodeRefOrNull(name);
} }
} }
return authority; return authority;

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>