mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Merged V1.3 to V1.4 (Workflow changes required)
svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4152 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4153 . svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4163 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4164 . svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4178 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4179 . svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4328 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4329 . svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4331 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4332 . git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4529 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
33
config/alfresco/extension/workflow-context.xml.sample
Normal file
33
config/alfresco/extension/workflow-context.xml.sample
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
|
||||||
|
|
||||||
|
<beans>
|
||||||
|
|
||||||
|
<bean id="sample.workflowBootstrap" parent="workflowDeployer">
|
||||||
|
<property name="workflowDefinitions">
|
||||||
|
<list>
|
||||||
|
<props>
|
||||||
|
<prop key="engineId">jbpm</prop>
|
||||||
|
<prop key="location">alfresco/workflow/parallelreview_processdefinition.xml</prop>
|
||||||
|
<prop key="mimetype">text/xml</prop>
|
||||||
|
<prop key="redeploy">false</prop>
|
||||||
|
</props>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
<property name="models">
|
||||||
|
<list>
|
||||||
|
<!-- NOTE: The above process definition relies on the default workflowModel.xml -->
|
||||||
|
<!-- which is already registered during Alfresco startup. -->
|
||||||
|
<!-- See bootstrap-context.xml (workflowBootstrap). -->
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
<property name="labels">
|
||||||
|
<list>
|
||||||
|
<!-- NOTE: The above process definition relies on the default workflow-messages.properties -->
|
||||||
|
<!-- which is already registered during Alfresco startup -->
|
||||||
|
<!-- See bootstrap-context.xml (workflowBootstrap). -->
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
</beans>
|
@@ -17,14 +17,8 @@
|
|||||||
<task name="wf:adhocTask" swimlane="assignee">
|
<task name="wf:adhocTask" swimlane="assignee">
|
||||||
<event type="task-create">
|
<event type="task-create">
|
||||||
<script>
|
<script>
|
||||||
if (bpm_workflowDueDate != void)
|
if (bpm_workflowDueDate != void) taskInstance.dueDate = bpm_workflowDueDate;
|
||||||
{
|
if (bpm_workflowPriority != void) taskInstance.priority = bpm_workflowPriority;
|
||||||
taskInstance.dueDate = bpm_workflowDueDate;
|
|
||||||
}
|
|
||||||
if (bpm_workflowPriority != void)
|
|
||||||
{
|
|
||||||
taskInstance.priority = bpm_workflowPriority;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
</event>
|
</event>
|
||||||
</task>
|
</task>
|
||||||
|
@@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="wf:parallelreview">
|
||||||
|
|
||||||
|
<swimlane name="initiator"></swimlane>
|
||||||
|
|
||||||
|
<start-state name="start">
|
||||||
|
<task name="wf:submitParallelReviewTask" swimlane="initiator" />
|
||||||
|
<transition name="" to="startreview">
|
||||||
|
<script>
|
||||||
|
<variable name="wf_approveCount" access="write" />
|
||||||
|
<expression>
|
||||||
|
wf_approveCount = 0;
|
||||||
|
</expression>
|
||||||
|
</script>
|
||||||
|
</transition>
|
||||||
|
</start-state>
|
||||||
|
|
||||||
|
<node name="startreview">
|
||||||
|
<action class="org.alfresco.repo.workflow.jbpm.ForEachFork">
|
||||||
|
<foreach>#{bpm_assignees}</foreach>
|
||||||
|
<var>reviewer</var>
|
||||||
|
</action>
|
||||||
|
<transition name="review" to="review" />
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<task-node name="review">
|
||||||
|
<task name="wf:reviewTask">
|
||||||
|
<event type="task-create">
|
||||||
|
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
|
||||||
|
<script>
|
||||||
|
taskInstance.actorId = reviewer.properties["cm:userName"];
|
||||||
|
</script>
|
||||||
|
</action>
|
||||||
|
</event>
|
||||||
|
</task>
|
||||||
|
<transition name="reject" to="endreview" />
|
||||||
|
<transition name="approve" to="endreview">
|
||||||
|
<script>
|
||||||
|
<variable name="wf_approveCount" access="read,write" />
|
||||||
|
<expression>
|
||||||
|
wf_approveCount = wf_approveCount +1;
|
||||||
|
</expression>
|
||||||
|
</script>
|
||||||
|
</transition>
|
||||||
|
</task-node>
|
||||||
|
|
||||||
|
<join name="endreview">
|
||||||
|
<transition to="isapproved" />
|
||||||
|
</join>
|
||||||
|
|
||||||
|
<decision name="isapproved">
|
||||||
|
<event type="node-enter">
|
||||||
|
<script>
|
||||||
|
<variable name="wf_reviewerCount" access="write"/>
|
||||||
|
<variable name="wf_requiredPercent" access="write"/>
|
||||||
|
<variable name="wf_actualPercent" access="write"/>
|
||||||
|
<expression>
|
||||||
|
wf_requiredPercent = wf_requiredApprovePercent;
|
||||||
|
wf_reviewerCount = bpm_assignees.size();
|
||||||
|
wf_actualPercent = ((wf_approveCount * 100) / wf_reviewerCount);
|
||||||
|
</expression>
|
||||||
|
</script>
|
||||||
|
</event>
|
||||||
|
<transition name="reject" to="rejected" />
|
||||||
|
<transition name="approve" to="approved">
|
||||||
|
<condition>#{wf_actualPercent >= wf_requiredApprovePercent}</condition>
|
||||||
|
</transition>
|
||||||
|
</decision>
|
||||||
|
|
||||||
|
<task-node name="rejected">
|
||||||
|
<task name="wf:rejectedParallelTask" swimlane="initiator" />
|
||||||
|
<transition to="end" />
|
||||||
|
</task-node>
|
||||||
|
|
||||||
|
<task-node name="approved">
|
||||||
|
<task name="wf:approvedParallelTask" swimlane="initiator" />
|
||||||
|
<transition to="end" />
|
||||||
|
</task-node>
|
||||||
|
|
||||||
|
<end-state name="end"/>
|
||||||
|
|
||||||
|
</process-definition>
|
@@ -17,14 +17,8 @@
|
|||||||
<task name="wf:reviewTask" swimlane="reviewer">
|
<task name="wf:reviewTask" swimlane="reviewer">
|
||||||
<event type="task-create">
|
<event type="task-create">
|
||||||
<script>
|
<script>
|
||||||
if (bpm_workflowDueDate != void)
|
if (bpm_workflowDueDate != void) taskInstance.dueDate = bpm_workflowDueDate;
|
||||||
{
|
if (bpm_workflowPriority != void) taskInstance.priority = bpm_workflowPriority;
|
||||||
taskInstance.dueDate = bpm_workflowDueDate;
|
|
||||||
}
|
|
||||||
if (bpm_workflowPriority != void)
|
|
||||||
{
|
|
||||||
taskInstance.priority = bpm_workflowPriority;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
</event>
|
</event>
|
||||||
</task>
|
</task>
|
||||||
|
@@ -35,6 +35,41 @@ wf_review.task.wf_approvedTask.description=Approved
|
|||||||
wf_review.node.end.title=End
|
wf_review.node.end.title=End
|
||||||
wf_review.node.end.description=End
|
wf_review.node.end.description=End
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Parallel Review Workflow
|
||||||
|
#
|
||||||
|
|
||||||
|
wf_parallelreview.workflow.title=Group Review & Approve
|
||||||
|
wf_parallelreview.workflow.description=Group Review & approval of content
|
||||||
|
|
||||||
|
# Parallel Review & Approve Task Definitions
|
||||||
|
|
||||||
|
wf_workflowmodel.type.wf_submitParallelReviewTask.title=Start Group Review
|
||||||
|
wf_workflowmodel.type.wf_submitParallelReviewTask.description=Submit documents for review & approval to a group of people
|
||||||
|
wf_workflowmodel.property.wf_requiredApprovePercent.title=Required approval percentage
|
||||||
|
wf_workflowmodel.property.wf_requiredApprovePercent.description=Percentage of reviewers who must approve for approval
|
||||||
|
wf_workflowmodel.type.wf_rejectedParallelTask.title=Rejected
|
||||||
|
wf_workflowmodel.type.wf_rejectedParallelTask.description=Rejected
|
||||||
|
wf_workflowmodel.type.wf_approvedParallelTask.title=Approved
|
||||||
|
wf_workflowmodel.type.wf_approvedParallelTask.description=Approved
|
||||||
|
wf_workflowmodel.property.wf_reviewerCount.title=Number of reviewers
|
||||||
|
wf_workflowmodel.property.wf_reviewerCount.description=Number of reviewers
|
||||||
|
wf_workflowmodel.property.wf_requiredPercent.title=Required approval percentage
|
||||||
|
wf_workflowmodel.property.wf_requiredPercent.description=Required approval percentage
|
||||||
|
wf_workflowmodel.property.wf_approveCount.title=Reviewers who approved
|
||||||
|
wf_workflowmodel.property.wf_approveCount.description=Reviewers who approved
|
||||||
|
wf_workflowmodel.property.wf_actualPercent.title=Actual approval percentage
|
||||||
|
wf_workflowmodel.property.wf_actualPercentdescription=Actual approval percentage
|
||||||
|
|
||||||
|
# Group Review & Approve Process Definitions
|
||||||
|
|
||||||
|
wf_parallelreview.node.review.transition.reject.title=Reject
|
||||||
|
wf_parallelreview.node.review.transition.reject.description=Reject
|
||||||
|
wf_parallelreview.node.review.transition.approve.title=Approve
|
||||||
|
wf_parallelreview.node.review.transition.approve.description=Approve
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Adhoc Task Workflow
|
# Adhoc Task Workflow
|
||||||
#
|
#
|
||||||
|
@@ -24,6 +24,26 @@
|
|||||||
</mandatory-aspects>
|
</mandatory-aspects>
|
||||||
</type>
|
</type>
|
||||||
|
|
||||||
|
<type name="wf:submitParallelReviewTask">
|
||||||
|
<parent>bpm:startTask</parent>
|
||||||
|
<properties>
|
||||||
|
<property name="wf:requiredApprovePercent">
|
||||||
|
<type>d:int</type>
|
||||||
|
<mandatory>true</mandatory>
|
||||||
|
<default>50</default>
|
||||||
|
<constraints>
|
||||||
|
<constraint type="MINMAX">
|
||||||
|
<parameter name="minValue"><value>1</value></parameter>
|
||||||
|
<parameter name="maxValue"><value>100</value></parameter>
|
||||||
|
</constraint>
|
||||||
|
</constraints>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
<mandatory-aspects>
|
||||||
|
<aspect>bpm:assignees</aspect>
|
||||||
|
</mandatory-aspects>
|
||||||
|
</type>
|
||||||
|
|
||||||
<type name="wf:reviewTask">
|
<type name="wf:reviewTask">
|
||||||
<parent>bpm:workflowTask</parent>
|
<parent>bpm:workflowTask</parent>
|
||||||
<overrides>
|
<overrides>
|
||||||
@@ -33,6 +53,20 @@
|
|||||||
</overrides>
|
</overrides>
|
||||||
</type>
|
</type>
|
||||||
|
|
||||||
|
<type name="wf:rejectedParallelTask">
|
||||||
|
<parent>bpm:workflowTask</parent>
|
||||||
|
<mandatory-aspects>
|
||||||
|
<aspect>wf:parallelReviewStats</aspect>
|
||||||
|
</mandatory-aspects>
|
||||||
|
</type>
|
||||||
|
|
||||||
|
<type name="wf:approvedParallelTask">
|
||||||
|
<parent>bpm:workflowTask</parent>
|
||||||
|
<mandatory-aspects>
|
||||||
|
<aspect>wf:parallelReviewStats</aspect>
|
||||||
|
</mandatory-aspects>
|
||||||
|
</type>
|
||||||
|
|
||||||
|
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<!-- Adhoc Tasks -->
|
<!-- Adhoc Tasks -->
|
||||||
@@ -86,4 +120,25 @@
|
|||||||
|
|
||||||
</types>
|
</types>
|
||||||
|
|
||||||
|
<aspects>
|
||||||
|
|
||||||
|
<aspect name="wf:parallelReviewStats">
|
||||||
|
<properties>
|
||||||
|
<property name="wf:reviewerCount">
|
||||||
|
<type>d:int</type>
|
||||||
|
</property>
|
||||||
|
<property name="wf:requiredPercent">
|
||||||
|
<type>d:int</type>
|
||||||
|
</property>
|
||||||
|
<property name="wf:approveCount">
|
||||||
|
<type>d:int</type>
|
||||||
|
</property>
|
||||||
|
<property name="wf:actualPercent">
|
||||||
|
<type>d:int</type>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
</aspect>
|
||||||
|
|
||||||
|
</aspects>
|
||||||
|
|
||||||
</model>
|
</model>
|
@@ -380,10 +380,10 @@ public class WorkflowInterpreter
|
|||||||
out.println("description: " + task.description);
|
out.println("description: " + task.description);
|
||||||
out.println("state: " + task.state);
|
out.println("state: " + task.state);
|
||||||
out.println("path: " + task.path.id);
|
out.println("path: " + task.path.id);
|
||||||
out.println("transitions: " + task.path.node.transitions.length);
|
out.println("transitions: " + task.definition.node.transitions.length);
|
||||||
for (WorkflowTransition transition : task.path.node.transitions)
|
for (WorkflowTransition transition : task.definition.node.transitions)
|
||||||
{
|
{
|
||||||
out.println(" transition: " + ((transition == null || transition.id.equals("")) ? "[default]" : transition.id) + " , title: " + transition.title + " , desc: " + transition.description);
|
out.println(" transition: " + ((transition.id == null || transition.id.equals("")) ? "[default]" : transition.id) + " , title: " + transition.title + " , desc: " + transition.description);
|
||||||
}
|
}
|
||||||
out.println("properties: " + task.properties.size());
|
out.println("properties: " + task.properties.size());
|
||||||
for (Map.Entry<QName, Serializable> prop : task.properties.entrySet())
|
for (Map.Entry<QName, Serializable> prop : task.properties.entrySet())
|
||||||
@@ -430,7 +430,7 @@ public class WorkflowInterpreter
|
|||||||
}
|
}
|
||||||
out.println("deployed definition id: " + def.id + " , name: " + def.name + " , title: " + def.title + " , version: " + def.version);
|
out.println("deployed definition id: " + def.id + " , name: " + def.name + " , title: " + def.title + " , version: " + def.version);
|
||||||
currentDeploy = command[1];
|
currentDeploy = command[1];
|
||||||
out.print(interpretCommand("use " + def.id));
|
out.print(interpretCommand("use definition " + def.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (command[0].equals("redeploy"))
|
else if (command[0].equals("redeploy"))
|
||||||
@@ -520,6 +520,10 @@ public class WorkflowInterpreter
|
|||||||
return "Syntax Error.\n";
|
return "Syntax Error.\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (currentWorkflowDef == null)
|
||||||
|
{
|
||||||
|
return "Workflow definition not selected.\n";
|
||||||
|
}
|
||||||
WorkflowPath path = workflowService.startWorkflow(currentWorkflowDef.id, params);
|
WorkflowPath path = workflowService.startWorkflow(currentWorkflowDef.id, params);
|
||||||
out.println("started workflow id: " + path.instance.id + ", path: " + path.id + " , node: " + path.node.name + " , def: " + path.instance.definition.title);
|
out.println("started workflow id: " + path.instance.id + ", path: " + path.id + " , node: " + path.node.name + " , def: " + path.instance.definition.title);
|
||||||
currentPath = path;
|
currentPath = path;
|
||||||
|
192
source/java/org/alfresco/repo/workflow/jbpm/ForEachFork.java
Normal file
192
source/java/org/alfresco/repo/workflow/jbpm/ForEachFork.java
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* 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.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For each "item in collection", create a fork.
|
||||||
|
*/
|
||||||
|
public class ForEachFork implements ActionHandler
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 4643103713602441652L;
|
||||||
|
|
||||||
|
private Element foreach;
|
||||||
|
private String var;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new child token for each item in list.
|
||||||
|
*
|
||||||
|
* @param executionContext
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void execute(final ExecutionContext executionContext)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// process action handler arguments
|
||||||
|
//
|
||||||
|
|
||||||
|
if (foreach == null)
|
||||||
|
{
|
||||||
|
throw new WorkflowException("forEach has not been provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
// build "for each" collection
|
||||||
|
List forEachColl = null;
|
||||||
|
String forEachCollStr = foreach.getTextTrim();
|
||||||
|
if (forEachCollStr != null)
|
||||||
|
{
|
||||||
|
if (forEachCollStr.startsWith("#{"))
|
||||||
|
{
|
||||||
|
Object eval = JbpmExpressionEvaluator.evaluate(forEachCollStr, executionContext);
|
||||||
|
if (eval == null)
|
||||||
|
{
|
||||||
|
throw new WorkflowException("forEach expression '" + forEachCollStr + "' evaluates to null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// expression evaluates to string
|
||||||
|
if (eval instanceof String)
|
||||||
|
{
|
||||||
|
String[] forEachStrs = ((String)eval).trim().split(",");
|
||||||
|
forEachColl = new ArrayList(forEachStrs.length);
|
||||||
|
for (String forEachStr : forEachStrs)
|
||||||
|
{
|
||||||
|
forEachColl.add(forEachStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// expression evaluates to collection
|
||||||
|
else if (eval instanceof Collection)
|
||||||
|
{
|
||||||
|
forEachColl = (List)eval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
forEachColl = (List)FieldInstantiator.getValue(List.class, foreach);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var == null || var.length() == 0)
|
||||||
|
{
|
||||||
|
throw new WorkflowException("forEach variable name has not been provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// create forked paths
|
||||||
|
//
|
||||||
|
|
||||||
|
Token rootToken = executionContext.getToken();
|
||||||
|
Node node = executionContext.getNode();
|
||||||
|
List<ForkedTransition> forkTransitions = new ArrayList<ForkedTransition>();
|
||||||
|
|
||||||
|
// first, create a new token and execution context for each item in list
|
||||||
|
for (int i = 0; i < node.getLeavingTransitions().size(); i++)
|
||||||
|
{
|
||||||
|
Transition transition = (Transition) node.getLeavingTransitions().get(i);
|
||||||
|
|
||||||
|
for (int iVar = 0; iVar < forEachColl.size(); iVar++)
|
||||||
|
{
|
||||||
|
// create child token to represent new path
|
||||||
|
String tokenName = getTokenName(rootToken, transition.getName(), iVar);
|
||||||
|
Token loopToken = new Token(rootToken, tokenName);
|
||||||
|
loopToken.setTerminationImplicit(true);
|
||||||
|
executionContext.getJbpmContext().getSession().save(loopToken);
|
||||||
|
|
||||||
|
// assign variable within path
|
||||||
|
final ExecutionContext newExecutionContext = new ExecutionContext(loopToken);
|
||||||
|
newExecutionContext.getContextInstance().createVariable(var, forEachColl.get(iVar), loopToken);
|
||||||
|
|
||||||
|
// record path & transition
|
||||||
|
ForkedTransition forkTransition = new ForkedTransition();
|
||||||
|
forkTransition.executionContext = newExecutionContext;
|
||||||
|
forkTransition.transition = transition;
|
||||||
|
forkTransitions.add(forkTransition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// let each new token leave the node.
|
||||||
|
//
|
||||||
|
for (ForkedTransition forkTransition : forkTransitions)
|
||||||
|
{
|
||||||
|
node.leave(forkTransition.executionContext, forkTransition.transition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a token name
|
||||||
|
*
|
||||||
|
* @param parent
|
||||||
|
* @param transitionName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected String getTokenName(Token parent, String transitionName, int loopIndex)
|
||||||
|
{
|
||||||
|
String tokenName = null;
|
||||||
|
if (transitionName != null)
|
||||||
|
{
|
||||||
|
if (!parent.hasChild(transitionName))
|
||||||
|
{
|
||||||
|
tokenName = transitionName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i = 2;
|
||||||
|
tokenName = transitionName + Integer.toString(i);
|
||||||
|
while (parent.hasChild(tokenName))
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
tokenName = transitionName + Integer.toString(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// no transition name
|
||||||
|
int size = ( parent.getChildren()!=null ? parent.getChildren().size()+1 : 1 );
|
||||||
|
tokenName = Integer.toString(size);
|
||||||
|
}
|
||||||
|
return tokenName + "." + loopIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fork Transition
|
||||||
|
*/
|
||||||
|
private class ForkedTransition
|
||||||
|
{
|
||||||
|
private ExecutionContext executionContext;
|
||||||
|
private Transition transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -68,6 +68,7 @@ import org.hibernate.proxy.HibernateProxy;
|
|||||||
import org.jbpm.JbpmContext;
|
import org.jbpm.JbpmContext;
|
||||||
import org.jbpm.JbpmException;
|
import org.jbpm.JbpmException;
|
||||||
import org.jbpm.context.exe.ContextInstance;
|
import org.jbpm.context.exe.ContextInstance;
|
||||||
|
import org.jbpm.context.exe.TokenVariableMap;
|
||||||
import org.jbpm.db.GraphSession;
|
import org.jbpm.db.GraphSession;
|
||||||
import org.jbpm.db.TaskMgmtSession;
|
import org.jbpm.db.TaskMgmtSession;
|
||||||
import org.jbpm.graph.def.Node;
|
import org.jbpm.graph.def.Node;
|
||||||
@@ -909,6 +910,18 @@ public class JBPMEngine extends BPMEngine
|
|||||||
TaskMgmtSession taskSession = context.getTaskMgmtSession();
|
TaskMgmtSession taskSession = context.getTaskMgmtSession();
|
||||||
TaskInstance taskInstance = getTaskInstance(taskSession, taskId);
|
TaskInstance taskInstance = getTaskInstance(taskSession, taskId);
|
||||||
|
|
||||||
|
// ensure all mandatory properties have been provided
|
||||||
|
QName[] missingProps = getMissingMandatoryTaskProperties(taskInstance);
|
||||||
|
if (missingProps != null && missingProps.length > 0)
|
||||||
|
{
|
||||||
|
String props = "";
|
||||||
|
for (int i = 0; i < missingProps.length; i++)
|
||||||
|
{
|
||||||
|
props += missingProps[i].toString() + ((i < missingProps.length -1) ? "," : "");
|
||||||
|
}
|
||||||
|
throw new WorkflowException("Mandatory task properties have not been provided: " + props);
|
||||||
|
}
|
||||||
|
|
||||||
// signal the transition on the task
|
// signal the transition on the task
|
||||||
if (transition == null)
|
if (transition == null)
|
||||||
{
|
{
|
||||||
@@ -1190,10 +1203,32 @@ public class JBPMEngine extends BPMEngine
|
|||||||
Map<QName, PropertyDefinition> taskProperties = taskDef.getProperties();
|
Map<QName, PropertyDefinition> taskProperties = taskDef.getProperties();
|
||||||
Map<QName, AssociationDefinition> taskAssocs = taskDef.getAssociations();
|
Map<QName, AssociationDefinition> taskAssocs = taskDef.getAssociations();
|
||||||
|
|
||||||
|
// build properties from jBPM context (visit all tokens to the root)
|
||||||
|
Map<String, Object> vars = instance.getVariablesLocally();
|
||||||
|
if (!localProperties)
|
||||||
|
{
|
||||||
|
ContextInstance context = instance.getContextInstance();
|
||||||
|
Token token = instance.getToken();
|
||||||
|
while (token != null)
|
||||||
|
{
|
||||||
|
TokenVariableMap varMap = context.getTokenVariableMap(token);
|
||||||
|
if (varMap != null)
|
||||||
|
{
|
||||||
|
Map<String, Object> tokenVars = varMap.getVariablesLocally();
|
||||||
|
for (Map.Entry<String, Object> entry : tokenVars.entrySet())
|
||||||
|
{
|
||||||
|
if (!vars.containsKey(entry.getKey()))
|
||||||
|
{
|
||||||
|
vars.put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token = token.getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// map arbitrary task variables
|
// map arbitrary task variables
|
||||||
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(10);
|
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(10);
|
||||||
Map<String, Object> vars = (localProperties ? instance.getVariablesLocally() : instance.getVariables());
|
|
||||||
|
|
||||||
for (Entry<String, Object> entry : vars.entrySet())
|
for (Entry<String, Object> entry : vars.entrySet())
|
||||||
{
|
{
|
||||||
String key = entry.getKey();
|
String key = entry.getKey();
|
||||||
@@ -1544,6 +1579,69 @@ public class JBPMEngine extends BPMEngine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get missing mandatory properties on Task
|
||||||
|
*
|
||||||
|
* @param instance task instance
|
||||||
|
* @return array of missing property names (or null, if none)
|
||||||
|
*/
|
||||||
|
protected QName[] getMissingMandatoryTaskProperties(TaskInstance instance)
|
||||||
|
{
|
||||||
|
List<QName> missingProps = null;
|
||||||
|
|
||||||
|
// retrieve properties of task
|
||||||
|
Map<QName, Serializable> existingValues = getTaskProperties(instance, false);
|
||||||
|
|
||||||
|
// retrieve definition of task
|
||||||
|
ClassDefinition classDef = getAnonymousTaskDefinition(getTaskDefinition(instance.getTask()));
|
||||||
|
Map<QName, PropertyDefinition> propertyDefs = classDef.getProperties();
|
||||||
|
Map<QName, AssociationDefinition> assocDefs = classDef.getAssociations();
|
||||||
|
|
||||||
|
// for each property, determine if it is mandatory
|
||||||
|
for (Map.Entry<QName, PropertyDefinition> entry : propertyDefs.entrySet())
|
||||||
|
{
|
||||||
|
QName name = entry.getKey();
|
||||||
|
if (!(name.getNamespaceURI().equals(NamespaceService.CONTENT_MODEL_1_0_URI) || (name.getNamespaceURI().equals(NamespaceService.SYSTEM_MODEL_1_0_URI))))
|
||||||
|
{
|
||||||
|
boolean isMandatory = entry.getValue().isMandatory();
|
||||||
|
if (isMandatory)
|
||||||
|
{
|
||||||
|
Object value = existingValues.get(entry.getKey());
|
||||||
|
if (value == null || (value instanceof String && ((String)value).length() == 0))
|
||||||
|
{
|
||||||
|
if (missingProps == null)
|
||||||
|
{
|
||||||
|
missingProps = new ArrayList<QName>();
|
||||||
|
}
|
||||||
|
missingProps.add(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Map.Entry<QName, AssociationDefinition> entry : assocDefs.entrySet())
|
||||||
|
{
|
||||||
|
QName name = entry.getKey();
|
||||||
|
if (!(name.getNamespaceURI().equals(NamespaceService.CONTENT_MODEL_1_0_URI) || (name.getNamespaceURI().equals(NamespaceService.SYSTEM_MODEL_1_0_URI))))
|
||||||
|
{
|
||||||
|
boolean isMandatory = entry.getValue().isTargetMandatory();
|
||||||
|
if (isMandatory)
|
||||||
|
{
|
||||||
|
Object value = existingValues.get(entry.getKey());
|
||||||
|
if (value == null || (value instanceof List && ((List)value).size() == 0))
|
||||||
|
{
|
||||||
|
if (missingProps == null)
|
||||||
|
{
|
||||||
|
missingProps = new ArrayList<QName>();
|
||||||
|
}
|
||||||
|
missingProps.add(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (missingProps == null) ? null : missingProps.toArray(new QName[missingProps.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a Repository association to JBPMNodeList or JBPMNode
|
* Convert a Repository association to JBPMNodeList or JBPMNode
|
||||||
*
|
*
|
||||||
|
@@ -32,6 +32,7 @@ import org.alfresco.repo.workflow.BPMEngineRegistry;
|
|||||||
import org.alfresco.repo.workflow.TaskComponent;
|
import org.alfresco.repo.workflow.TaskComponent;
|
||||||
import org.alfresco.repo.workflow.WorkflowComponent;
|
import org.alfresco.repo.workflow.WorkflowComponent;
|
||||||
import org.alfresco.repo.workflow.WorkflowModel;
|
import org.alfresco.repo.workflow.WorkflowModel;
|
||||||
|
import org.alfresco.repo.workflow.WorkflowPackageComponent;
|
||||||
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.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
@@ -60,6 +61,7 @@ public class JBPMEngineTest extends BaseSpringTest
|
|||||||
NodeService nodeService;
|
NodeService nodeService;
|
||||||
WorkflowComponent workflowComponent;
|
WorkflowComponent workflowComponent;
|
||||||
TaskComponent taskComponent;
|
TaskComponent taskComponent;
|
||||||
|
WorkflowPackageComponent packageComponent;
|
||||||
WorkflowDefinition testWorkflowDef;
|
WorkflowDefinition testWorkflowDef;
|
||||||
NodeRef testNodeRef;
|
NodeRef testNodeRef;
|
||||||
|
|
||||||
@@ -74,6 +76,7 @@ public class JBPMEngineTest extends BaseSpringTest
|
|||||||
BPMEngineRegistry registry = (BPMEngineRegistry)applicationContext.getBean("bpm_engineRegistry");
|
BPMEngineRegistry registry = (BPMEngineRegistry)applicationContext.getBean("bpm_engineRegistry");
|
||||||
workflowComponent = registry.getWorkflowComponent("jbpm");
|
workflowComponent = registry.getWorkflowComponent("jbpm");
|
||||||
taskComponent = registry.getTaskComponent("jbpm");
|
taskComponent = registry.getTaskComponent("jbpm");
|
||||||
|
packageComponent = (WorkflowPackageComponent)applicationContext.getBean("workflowPackageImpl");
|
||||||
|
|
||||||
// deploy test process messages
|
// deploy test process messages
|
||||||
I18NUtil.registerResourceBundle("org/alfresco/repo/workflow/jbpm/test-messages");
|
I18NUtil.registerResourceBundle("org/alfresco/repo/workflow/jbpm/test-messages");
|
||||||
@@ -357,6 +360,7 @@ public class JBPMEngineTest extends BaseSpringTest
|
|||||||
Map<QName, Serializable> parameters = new HashMap<QName, Serializable>();
|
Map<QName, Serializable> parameters = new HashMap<QName, Serializable>();
|
||||||
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "reviewer"), "admin");
|
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "reviewer"), "admin");
|
||||||
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
|
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
|
||||||
|
parameters.put(QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "package"), packageComponent.createPackage(null));
|
||||||
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
|
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
|
||||||
assertNotNull(path);
|
assertNotNull(path);
|
||||||
List<WorkflowTask> tasks = workflowComponent.getTasksForWorkflowPath(path.id);
|
List<WorkflowTask> tasks = workflowComponent.getTasksForWorkflowPath(path.id);
|
||||||
@@ -402,6 +406,7 @@ public class JBPMEngineTest extends BaseSpringTest
|
|||||||
Map<QName, Serializable> parameters = new HashMap<QName, Serializable>();
|
Map<QName, Serializable> parameters = new HashMap<QName, Serializable>();
|
||||||
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "reviewer"), "admin");
|
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "reviewer"), "admin");
|
||||||
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
|
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
|
||||||
|
parameters.put(QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "package"), packageComponent.createPackage(null));
|
||||||
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
|
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
|
||||||
assertNotNull(path);
|
assertNotNull(path);
|
||||||
List<WorkflowTask> tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
|
List<WorkflowTask> tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
|
||||||
@@ -443,6 +448,7 @@ public class JBPMEngineTest extends BaseSpringTest
|
|||||||
Map<QName, Serializable> parameters = new HashMap<QName, Serializable>();
|
Map<QName, Serializable> parameters = new HashMap<QName, Serializable>();
|
||||||
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "reviewer"), "admin");
|
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "reviewer"), "admin");
|
||||||
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
|
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
|
||||||
|
parameters.put(QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "package"), packageComponent.createPackage(null));
|
||||||
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
|
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
|
||||||
assertNotNull(path);
|
assertNotNull(path);
|
||||||
List<WorkflowTask> tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
|
List<WorkflowTask> tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
|
||||||
@@ -466,6 +472,7 @@ public class JBPMEngineTest extends BaseSpringTest
|
|||||||
WorkflowDefinition workflowDef = deployment.definition;
|
WorkflowDefinition workflowDef = deployment.definition;
|
||||||
Map<QName, Serializable> parameters = new HashMap<QName, Serializable>();
|
Map<QName, Serializable> parameters = new HashMap<QName, Serializable>();
|
||||||
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
|
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
|
||||||
|
parameters.put(QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "package"), packageComponent.createPackage(null));
|
||||||
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
|
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
|
||||||
assertNotNull(path);
|
assertNotNull(path);
|
||||||
List<WorkflowTask> tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
|
List<WorkflowTask> tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
|
||||||
|
Reference in New Issue
Block a user