[jbpm-commits] JBoss JBPM SVN: r6152 - in jbpm4/trunk/modules/bpmn/src/main: java/org/jbpm/bpmn/parser and 1 other directories.
do-not-reply at jboss.org
do-not-reply at jboss.org
Mon Feb 1 16:47:17 EST 2010
Author: jbarrez
Date: 2010-02-01 16:47:17 -0500 (Mon, 01 Feb 2010)
New Revision: 6152
Modified:
jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/EndActivity.java
jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/IntermediateCatchTimerEvent.java
jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/NoneStartEventActivity.java
jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/SubProcessActivity.java
jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/SubProcessBinding.java
jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/TimerStartEventActivity.java
jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/parser/BpmnParser.java
jbpm4/trunk/modules/bpmn/src/main/resources/jbpm.bpmn.flownodes.xml
Log:
JBPM-2740: implemented embedded subprocess with basic test case
Modified: jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/EndActivity.java
===================================================================
--- jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/EndActivity.java 2010-01-30 04:33:30 UTC (rev 6151)
+++ jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/EndActivity.java 2010-02-01 21:47:17 UTC (rev 6152)
@@ -34,7 +34,7 @@
/**
* @author Tom Baeyens
*/
-public class EndActivity extends BpmnActivity {
+public class EndActivity extends BpmnActivity implements BpmnEvent {
private static final long serialVersionUID = 1L;
@@ -50,14 +50,10 @@
List<Transition> outgoingTransitions = activity.getOutgoingTransitions();
ActivityImpl parentActivity = (ActivityImpl) activity.getParentActivity();
- if ( (parentActivity!=null)
- && ("group".equals(parentActivity.getType()))
- ) {
+ if ( (parentActivity != null) && ("subProcess".equals(parentActivity.getType())) ) {
// if the end activity itself has an outgoing transition
// (such end activities should be drawn on the border of the group)
- if ( (outgoingTransitions!=null)
- && (outgoingTransitions.size()==1)
- ) {
+ if ( (outgoingTransitions != null) && (outgoingTransitions.size() == 1) ) {
Transition outgoingTransition = outgoingTransitions.get(0);
// taking the transition that goes over the group boundaries will
// destroy the scope automatically (see atomic operation TakeTransition)
Modified: jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/IntermediateCatchTimerEvent.java
===================================================================
--- jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/IntermediateCatchTimerEvent.java 2010-01-30 04:33:30 UTC (rev 6151)
+++ jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/IntermediateCatchTimerEvent.java 2010-02-01 21:47:17 UTC (rev 6152)
@@ -30,7 +30,7 @@
*
* @author Joram Barrez
*/
-public class IntermediateCatchTimerEvent extends BpmnActivity implements EventListener {
+public class IntermediateCatchTimerEvent extends BpmnActivity implements EventListener, BpmnEvent {
private static final long serialVersionUID = 1L;
Modified: jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/NoneStartEventActivity.java
===================================================================
--- jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/NoneStartEventActivity.java 2010-01-30 04:33:30 UTC (rev 6151)
+++ jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/NoneStartEventActivity.java 2010-02-01 21:47:17 UTC (rev 6152)
@@ -27,10 +27,10 @@
/**
* None start event
*/
-public class NoneStartEventActivity extends BpmnActivity {
+public class NoneStartEventActivity extends BpmnActivity implements BpmnEvent {
private static final long serialVersionUID = 1L;
-
+
public void execute(ActivityExecution execution) {
execute((ExecutionImpl) execution);
}
@@ -38,4 +38,5 @@
public void execute(ExecutionImpl execution) {
proceed(execution, findOutgoingSequenceFlow(execution, CONDITIONS_CHECKED));
}
+
}
Modified: jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/SubProcessActivity.java
===================================================================
--- jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/SubProcessActivity.java 2010-01-30 04:33:30 UTC (rev 6151)
+++ jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/SubProcessActivity.java 2010-02-01 21:47:17 UTC (rev 6152)
@@ -23,7 +23,11 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import org.jbpm.api.Execution;
+import org.jbpm.api.JbpmException;
+import org.jbpm.api.activity.ActivityExecution;
import org.jbpm.api.model.Activity;
import org.jbpm.pvm.internal.model.ExecutionImpl;
@@ -31,11 +35,36 @@
*
* @author Joram Barrez
*/
-public class SubProcessActivity extends BpmnActivity {
+public class SubProcessActivity extends BpmnExternalActivity {
private static final long serialVersionUID = 1L;
- public void execute(ExecutionImpl executionImpl) {
+ public void execute(ExecutionImpl execution) {
+ List<Activity> startActivities = findStartActivities(execution);
+ if (startActivities.size() == 1) {
+
+ execution.execute(startActivities.get(0));
+
+ } else if (startActivities.size() > 1) {
+
+ ExecutionImpl concurrentRoot = null;
+ if (Execution.STATE_ACTIVE_ROOT.equals(execution.getState())) {
+ concurrentRoot = execution;
+ } else if (Execution.STATE_ACTIVE_CONCURRENT.equals(execution.getState())) {
+ concurrentRoot = execution.getParent();
+ } else {
+ throw new JbpmException("illegal state for execution :" + execution.getState());
+ }
+
+ for (Activity startActivity: startActivities) {
+ ExecutionImpl concurrentExecution = concurrentRoot.createExecution();
+ concurrentExecution.setState(Execution.STATE_ACTIVE_CONCURRENT);
+ concurrentExecution.execute(startActivity);
+ }
+
+ } else {
+ throw new JbpmException("Could not find start activity for the sub-process " + execution.getActivityName());
+ }
}
/**
@@ -47,12 +76,17 @@
for (Activity nestedActivity : execution.getActivity().getActivities()) {
if ( (nestedActivity.getIncomingTransitions()==null)
- || (nestedActivity.getIncomingTransitions().isEmpty())) {
+ || (nestedActivity.getIncomingTransitions().isEmpty())) {
startActivities.add(nestedActivity);
}
}
return startActivities;
}
+
+ public void signal(ActivityExecution execution, String signalName, Map<String, ? > parameters) throws Exception {
+ ExecutionImpl executionImpl = (ExecutionImpl) execution;
+ proceed(executionImpl, findOutgoingSequenceFlow(executionImpl, CONDITIONS_CHECKED));
+ }
}
Modified: jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/SubProcessBinding.java
===================================================================
--- jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/SubProcessBinding.java 2010-01-30 04:33:30 UTC (rev 6151)
+++ jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/SubProcessBinding.java 2010-02-01 21:47:17 UTC (rev 6152)
@@ -21,6 +21,13 @@
*/
package org.jbpm.bpmn.flownodes;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.jbpm.api.activity.ActivityBehaviour;
+import org.jbpm.api.model.Activity;
+import org.jbpm.api.model.Transition;
+import org.jbpm.bpmn.model.BpmnProcessDefinition;
import org.jbpm.bpmn.parser.BpmnParser;
import org.jbpm.pvm.internal.model.ActivityImpl;
import org.jbpm.pvm.internal.xml.Parse;
@@ -33,7 +40,7 @@
protected static final String TAG_NAME = "subProcess";
- public SubProcessBinding(String tagName) {
+ public SubProcessBinding() {
super(TAG_NAME);
}
@@ -42,11 +49,68 @@
ActivityImpl activity = parse.contextStackFind(ActivityImpl.class);
bpmnParser.parseActivities(element, parse, activity);
+ bpmnParser.parseSequenceFlow(element, parse, parse.contextStackFind(BpmnProcessDefinition.class));
- // TODO only none start event is allowed!
+ validateStartActivities(activity, parse);
+ validateAllSequenceFlow(activity, parse);
subProcessActivity.setDefault(getDefault());
return subProcessActivity;
}
+
+ /**
+ * Only none start activities and activities without incoming sequence flow
+ * are allowed as start activities in a sub-process.
+ */
+ protected void validateStartActivities(ActivityImpl subProcessActivity, Parse parse) {
+ for (Activity childActivity : subProcessActivity.getActivities()) {
+ if (childActivity.getIncomingTransitions().isEmpty()) {
+ ActivityBehaviour activityBehaviour = ((ActivityImpl) childActivity).getActivityBehaviour();
+ if ( (activityBehaviour instanceof BpmnEvent)
+ && !(activityBehaviour instanceof NoneStartEventActivity) ) {
+ parse.addProblem("Only none start events are allowed in an embedded sub process");
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Sequence flow are not allowed to cross the sub-process boundary.
+ * Exception to that rule is sequence flow which have as target a none start event (which actually
+ * could graphically be viewed as boundary events).
+ */
+ protected void validateAllSequenceFlow(ActivityImpl subProcessActivity, Parse parse) {
+
+ // collect all child activity ids
+ Set<String> childActivityIds = new HashSet<String>();
+ for (Activity childActivity : subProcessActivity.getActivities()) {
+ childActivityIds.add(childActivity.getName());
+ }
+
+ // Verify source/target of all sequenceflow
+ for (Activity childActivity : subProcessActivity.getActivities()) {
+ for (Transition incomingTransition : childActivity.getIncomingTransitions()) {
+ validateSequenceFlow(incomingTransition, childActivityIds, parse);
+ }
+ for (Transition outgoingTransition : subProcessActivity.getOutgoingTransitions()) {
+ validateSequenceFlow(outgoingTransition, childActivityIds, parse);
+ }
+ }
+ }
+
+ protected void validateSequenceFlow(Transition transition, Set<String> subProcessActivityIds, Parse parse) {
+ if (!subProcessActivityIds.contains(transition.getSource().getName())) {
+ parse.addProblem("Invalid sequence flow " + transition.getName()
+ + ": cannot cross sub-process boundaries from " + transition.getSource().getName()
+ + " into the sub process activity.");
+ }
+ if (!subProcessActivityIds.contains(transition.getDestination().getName())) {
+ parse.addProblem("Invalid sequence flow " + transition.getName()
+ + ": cannot cross sub-process boundaries to " + transition.getSource().getName()
+ + " from within the sub process activity.");
+ }
+ }
+
}
Modified: jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/TimerStartEventActivity.java
===================================================================
--- jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/TimerStartEventActivity.java 2010-01-30 04:33:30 UTC (rev 6151)
+++ jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/TimerStartEventActivity.java 2010-02-01 21:47:17 UTC (rev 6152)
@@ -26,7 +26,7 @@
/**
* @author Joram Barrez
*/
-public class TimerStartEventActivity extends NoneStartEventActivity {
+public class TimerStartEventActivity extends NoneStartEventActivity implements BpmnEvent {
private static final long serialVersionUID = 1L;
Modified: jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/parser/BpmnParser.java
===================================================================
--- jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/parser/BpmnParser.java 2010-01-30 04:33:30 UTC (rev 6151)
+++ jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/parser/BpmnParser.java 2010-02-01 21:47:17 UTC (rev 6152)
@@ -27,16 +27,23 @@
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import javax.xml.namespace.QName;
import org.jbpm.api.JbpmException;
import org.jbpm.api.activity.ActivityBehaviour;
+import org.jbpm.api.model.Activity;
+import org.jbpm.api.model.Transition;
import org.jbpm.bpmn.common.Resource;
import org.jbpm.bpmn.common.ResourceParameter;
import org.jbpm.bpmn.flownodes.BpmnActivity;
import org.jbpm.bpmn.flownodes.BpmnBinding;
+import org.jbpm.bpmn.flownodes.BpmnEvent;
+import org.jbpm.bpmn.flownodes.NoneStartEventActivity;
+import org.jbpm.bpmn.flownodes.SubProcessActivity;
import org.jbpm.bpmn.model.BpmnProcessDefinition;
import org.jbpm.bpmn.model.SequenceflowCondition;
import org.jbpm.internal.log.Log;
@@ -145,6 +152,8 @@
// bind activities to their destinations
parseSequenceFlow(processElement, parse, processDefinition);
+
+ postParsingValidations(parse, processDefinition);
} finally {
parse.contextStackPop();
@@ -245,21 +254,40 @@
public void parseSequenceFlow(Element element, Parse parse, BpmnProcessDefinition processDefinition) {
List<Element> transitionElements = XmlUtil.elements(element, "sequenceFlow");
for (Element transitionElement : transitionElements) {
+
+ // Parse attributes
String transitionName = XmlUtil.attribute(transitionElement, "name", false, parse);
- String transitionId = XmlUtil.attribute(transitionElement, "id", false, parse); // id is not required
+ String transitionId = XmlUtil.attribute(transitionElement, "id", false, parse);
String sourceRef = XmlUtil.attribute(transitionElement, "sourceRef", true, parse);
String targetRef = XmlUtil.attribute(transitionElement, "targetRef", true, parse);
if (log.isDebugEnabled()) {
log.debug(transitionId + ": " + sourceRef + " -> " + targetRef);
}
- Element conditionElement = XmlUtil.element(transitionElement, "conditionExpression");
+
+ // Create new outgoing transition on sourceActivity
+ ActivityImpl sourceActivity = processDefinition.findActivity(sourceRef);
+ TransitionImpl transition = null;
+ if (sourceActivity != null) {
+ transition = sourceActivity.createOutgoingTransition();
+ transition.setName(transitionId);
+ transition.setDescription(transitionName);
+ } else {
+ parse.addProblem("SourceRef " + sourceRef + " cannot be found");
+ }
+
+ // Create incoming transition on targetActivity
+ ActivityImpl destinationActivity = processDefinition.findActivity(targetRef);
+ if (destinationActivity != null) {
+ destinationActivity.addIncomingTransition(transition);
+ } else {
+ parse.addProblem("TargetRef '" + targetRef + "' cannot be found");
+ }
- TransitionImpl transition = processDefinition.findActivity(sourceRef).createOutgoingTransition();
-
+ // Set default sequence flow if applicable
try {
// If something went wrong parsing the activity, there is no behaviour and an exception is thrown in .getBehaviour()
- ActivityBehaviour behaviour = processDefinition.findActivity(sourceRef).getActivityBehaviour();
+ ActivityBehaviour behaviour = sourceActivity.getActivityBehaviour();
if (behaviour instanceof BpmnActivity) {
BpmnActivity bpmnActivity = (BpmnActivity) behaviour;
String defaultSeqFlow = bpmnActivity.getDefault();
@@ -282,38 +310,34 @@
}
}
- if (conditionElement != null) {
- String type = conditionElement.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "type");
- if ("bpmn:tFormalExpression".equals(type) || "tFormalExpression".equals(type)) {
+ parseConditionOnSequenceFlow(parse, transitionElement, transitionId, transition);
- String expr = conditionElement.getTextContent();
- String lang = XmlUtil.attribute(conditionElement, "language");
- // TODO: add looking up the default language in the document under definitions if lang is null.
-
- SequenceflowCondition condition = new SequenceflowCondition();
- condition.setExpression(expr);
- condition.setLanguage(lang);
- transition.setCondition(condition);
+ processDefinition.addSequenceFlow(transitionId, transition);
+ }
+ }
- } else {
- parse.addProblem("Type of the conditionExpression on sequenceFlow with id=" +
- transitionId + " is of onsupported type 'bpmn:tExpression'", transitionElement);
- }
- }
+ public void parseConditionOnSequenceFlow(Parse parse, Element transitionElement, String transitionId, TransitionImpl transition) {
+ Element conditionElement = XmlUtil.element(transitionElement, "conditionExpression");
+ if (conditionElement != null) {
+ String type = conditionElement.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "type");
+ if ("bpmn:tFormalExpression".equals(type) || "tFormalExpression".equals(type)) {
- ActivityImpl activity = processDefinition.findActivity(targetRef);
- if (activity != null) {
- activity.addIncomingTransition(transition);
+ String expr = conditionElement.getTextContent();
+ String lang = XmlUtil.attribute(conditionElement, "language");
+ // TODO: add looking up the default language in the document under definitions if lang is null.
+
+ SequenceflowCondition condition = new SequenceflowCondition();
+ condition.setExpression(expr);
+ condition.setLanguage(lang);
+ transition.setCondition(condition);
+
} else {
- parse.addProblem("TargetRef '" + targetRef + "' cannot be found");
+ parse.addProblem("Type of the conditionExpression on sequenceFlow with id=" +
+ transitionId + " is of onsupported type 'bpmn:tExpression'", transitionElement);
}
-
- transition.setName(transitionId);
- transition.setDescription(transitionName);
-
- processDefinition.addSequenceFlow(transitionId, transition);
}
}
+
public void parseDefinition(Element documentElement, Parse parse) {
parseImports(documentElement, parse);
}
@@ -450,4 +474,84 @@
}
+ /**
+ * Validations that are called at the end of the parsing: all activities,
+ * sequence flow, etc are fully available to allow detailed validations.
+ */
+ public void postParsingValidations(Parse parse, BpmnProcessDefinition processDefinition) {
+ validateSubProcessBoundaryNotCrossed(parse, processDefinition);
+ }
+
+ /* ---------------------
+ * SUBPROCESS VALIDATION
+ * --------------------- */
+
+ /**
+ * Validates that the boundary of a sub-process isn't crossed.
+ */
+ protected void validateSubProcessBoundaryNotCrossed(Parse parse, BpmnProcessDefinition processDefinition) {
+ for (Activity activity : processDefinition.getActivities()) {
+ ActivityImpl activityImpl = (ActivityImpl) activity;
+ if ( activityImpl.getActivityBehaviour() instanceof SubProcessActivity) {
+ validateSubProcessStartActivities(activityImpl, parse);
+ validateSubProcessAllSequenceFlow(activityImpl, parse);
+ }
+ }
+ }
+
+ /**
+ * Only none start activities and activities without incoming sequence flow
+ * are allowed as start activities in a sub-process.
+ */
+ protected void validateSubProcessStartActivities(ActivityImpl subProcessActivity, Parse parse) {
+ for (Activity childActivity : subProcessActivity.getActivities()) {
+ if (childActivity.getIncomingTransitions().isEmpty()) {
+
+ ActivityBehaviour activityBehaviour = ((ActivityImpl) childActivity).getActivityBehaviour();
+ if ( (activityBehaviour instanceof BpmnEvent)
+ && !(activityBehaviour instanceof NoneStartEventActivity) ) {
+ parse.addProblem("Only none start events are allowed in an embedded sub process");
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Sequence flow are not allowed to cross the sub-process boundary.
+ * Exception to that rule is sequence flow which have as target a none start event (which actually
+ * could graphically be viewed as boundary events).
+ */
+ protected void validateSubProcessAllSequenceFlow(ActivityImpl subProcessActivity, Parse parse) {
+
+ // collect all child activity ids
+ Set<String> childActivityIds = new HashSet<String>();
+ for (Activity childActivity : subProcessActivity.getActivities()) {
+ childActivityIds.add(childActivity.getName());
+ }
+
+ // Verify source/target of all sequenceflow
+ for (Activity childActivity : subProcessActivity.getActivities()) {
+ for (Transition incomingTransition : childActivity.getIncomingTransitions()) {
+ validateSubProcessSequenceFlow(incomingTransition, childActivityIds, parse);
+ }
+ for (Transition outgoingTransition : childActivity.getOutgoingTransitions()) {
+ validateSubProcessSequenceFlow(outgoingTransition, childActivityIds, parse);
+ }
+ }
+ }
+
+ protected void validateSubProcessSequenceFlow(Transition transition, Set<String> subProcessActivityIds, Parse parse) {
+ if (!subProcessActivityIds.contains(transition.getSource().getName())) {
+ parse.addProblem("Invalid sequence flow " + transition.getName()
+ + ": cannot cross sub-process boundaries from " + transition.getSource().getName()
+ + " into the sub process activity.");
+ }
+ if (!subProcessActivityIds.contains(transition.getDestination().getName())) {
+ parse.addProblem("Invalid sequence flow " + transition.getName()
+ + ": cannot cross sub-process boundaries to " + transition.getSource().getName()
+ + " from within the sub process activity.");
+ }
+ }
+
}
Modified: jbpm4/trunk/modules/bpmn/src/main/resources/jbpm.bpmn.flownodes.xml
===================================================================
--- jbpm4/trunk/modules/bpmn/src/main/resources/jbpm.bpmn.flownodes.xml 2010-01-30 04:33:30 UTC (rev 6151)
+++ jbpm4/trunk/modules/bpmn/src/main/resources/jbpm.bpmn.flownodes.xml 2010-02-01 21:47:17 UTC (rev 6152)
@@ -14,6 +14,7 @@
<!-- Activities -->
<activity binding="org.jbpm.bpmn.flownodes.ReceiveBinding" />
+ <activity binding="org.jbpm.bpmn.flownodes.SubProcessBinding" />
<!-- Task bindings -->
<activity binding="org.jbpm.bpmn.flownodes.ManualTaskBinding" />
More information about the jbpm-commits
mailing list