[jbpm-commits] JBoss JBPM SVN: r6084 - in jbpm4/trunk/modules: bpmn/src/main/java/org/jbpm/bpmn/model and 5 other directories.
do-not-reply at jboss.org
do-not-reply at jboss.org
Fri Jan 15 19:21:04 EST 2010
Author: jbarrez
Date: 2010-01-15 19:21:04 -0500 (Fri, 15 Jan 2010)
New Revision: 6084
Added:
jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/bpmn/test/gateway/InclusiveGatewayTest.java
Modified:
jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/BpmnActivity.java
jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/InclusiveGatewayActivity.java
jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/ParallelGatewayActivity.java
jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/model/BpmnProcessDefinition.java
jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/parser/BpmnParser.java
jbpm4/trunk/modules/test-base/src/main/java/org/jbpm/test/assertion/CollectionAssertions.java
jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/bpmn/ParallelGatewayTest.java
jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/bpmn/test/gateway/ParallelGatewayMergeTest.java
jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/test/activity/forkjoin/MultipleForksTest.java
Log:
JBPM2738: working on the inclusive gateway. Covered basic split/merge behaviour with new logic and test cases.
Modified: jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/BpmnActivity.java
===================================================================
--- jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/BpmnActivity.java 2010-01-15 14:04:04 UTC (rev 6083)
+++ jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/BpmnActivity.java 2010-01-16 00:21:04 UTC (rev 6084)
@@ -107,14 +107,21 @@
}
}
+ /**
+ * Returns the list of outgoing sequence flow for this activity.
+ * If the boolean 'checkConditions' is true, conditions on the sequence flow will be evaluated.
+ *
+ * Note that for activities that have a default sequence flow (eg Exclusive gateway),
+ * the default sequence flow will NOT be included in the returned list.
+ */
protected List<Transition> findOutgoingSequenceFlow(ExecutionImpl execution, boolean checkConditions) {
Activity activity = execution.getActivity();
// evaluate the conditions and find the transitions that should be forked
List<Transition> forkingTransitions = new ArrayList<Transition>();
List<Transition> outgoingTransitions = activity.getOutgoingTransitions();
+
for (Transition transition : outgoingTransitions) {
Condition condition = transition.getCondition();
- // also ignore the default transition of the exclusive gateway
if ( ( (condition == null)
|| (!checkConditions)
|| (condition.evaluate(execution))
Modified: jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/InclusiveGatewayActivity.java
===================================================================
--- jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/InclusiveGatewayActivity.java 2010-01-15 14:04:04 UTC (rev 6083)
+++ jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/InclusiveGatewayActivity.java 2010-01-16 00:21:04 UTC (rev 6084)
@@ -21,9 +21,21 @@
*/
package org.jbpm.bpmn.flownodes;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.hibernate.LockMode;
+import org.hibernate.Session;
+import org.jbpm.api.Execution;
+import org.jbpm.api.JbpmException;
import org.jbpm.api.activity.ActivityExecution;
+import org.jbpm.bpmn.model.BpmnProcessDefinition;
import org.jbpm.internal.log.Log;
+import org.jbpm.pvm.internal.env.EnvironmentImpl;
+import org.jbpm.pvm.internal.model.Activity;
import org.jbpm.pvm.internal.model.ExecutionImpl;
+import org.jbpm.pvm.internal.model.Transition;
/**
* @author Joram Barrez
@@ -33,6 +45,8 @@
private static final long serialVersionUID = 1L;
private static final Log LOG = Log.getLog(InclusiveGatewayActivity.class.getName());
+
+ protected LockMode lockMode = LockMode.UPGRADE;
public void execute(ActivityExecution execution) throws Exception {
execute((ExecutionImpl) execution);
@@ -40,14 +54,147 @@
public void execute(ExecutionImpl execution) {
int nrOfIncoming = execution.getActivity().getIncomingTransitions().size();
+
if (nrOfIncoming == 1) { // no merge behaviour needed, save some time and do a fork immediately
if (LOG.isDebugEnabled()) {
LOG.debug("Only one incoming sequence flow found. Executing fork logic only.");
}
- proceed(execution, findOutgoingSequenceFlow(execution, CONDITIONS_CHECKED));
- }
+ fork(execution);
+ } else {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Multiple incoming sequence flow found. Executing join logic.");
+ }
+ boolean allExecutionsArrived = handleIncomingExecution(execution);
+
+ if (allExecutionsArrived) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("All executions have reached the inclusive join. Executing fork logic.");
+ }
+ ExecutionImpl outgoingExecution = join(execution);
+ fork(outgoingExecution);
+ }
+
+ }
- // TODO finish
+
}
+
+ public void fork(ExecutionImpl execution) {
+ List<Transition> outgointSeqFlow = findOutgoingSequenceFlow(execution, CONDITIONS_CHECKED);
+
+ if (outgointSeqFlow.isEmpty()) {
+ Transition defaultSeqFlow = execution.getActivity().getDefaultOutgoingTransition();
+ if (defaultSeqFlow != null) {
+ outgointSeqFlow.add(defaultSeqFlow);
+ } else {
+ throw new JbpmException("No sequenceFlow condition evaluated to true for " +
+ execution.getActivity() + " and no default sequenceFlow was speficied");
+ }
+ }
+
+ proceed(execution, outgointSeqFlow);
+ }
+
+ /**
+ * Joins the incoming executions.
+ * Returns true if all executions have reached the gateway.
+ */
+ protected boolean handleIncomingExecution(ExecutionImpl execution) {
+ if (Execution.STATE_ACTIVE_CONCURRENT.equals(execution.getState())) {
+
+ // force version increment in the parent execution
+ Session session = EnvironmentImpl.getFromCurrent(Session.class);
+ session.lock(execution.getParent(), lockMode);
+ execution.setState(Execution.STATE_INACTIVE_JOIN);
+ execution.waitForSignal();
+
+ return isComplete(execution);
+
+ } else {
+ throw new JbpmException("invalid execution state: " + execution.getState());
+ }
+ }
+
+ /**
+ * Joins all the incoming executions currently waiting at the gateway.
+ *
+ * @return An execution that can be used to leave the gateway (one outgoing sequence flow)
+ * or to create child executions on (fork behaviour when multiple outgoing sequence flow).
+ */
+ protected ExecutionImpl join(ExecutionImpl execution) {
+ Activity activity = execution.getActivity();
+ ExecutionImpl concurrentRoot = execution.getParent();
+ List<ExecutionImpl> joinedExecutions = getJoinedExecutions(concurrentRoot, activity);
+
+ endJoinedExecutions(joinedExecutions);
+
+ ExecutionImpl outgoingExecution = null;
+ if (concurrentRoot.getExecutions().size() == 0) {
+ outgoingExecution = concurrentRoot;
+ outgoingExecution.setState(Execution.STATE_ACTIVE_ROOT);
+ } else {
+ outgoingExecution = concurrentRoot.createExecution();
+ outgoingExecution.setState(Execution.STATE_ACTIVE_CONCURRENT);
+ }
+
+ outgoingExecution.setActivity(activity);
+ return outgoingExecution;
+ }
+
+ /**
+ * Section 14.3.2 of the BPMN 2.0 specification.
+ *
+ * The Inclusive Gateway is activated if
+ * - At least one incoming sequence flow has at least one Token and
+ * - for each empty incoming sequence flow, there is no Token in the graph anywhere
+ * upstream of this sequence flow, i.e., there is no directed path (formed by Sequence Flow)
+ * from a Token to this sequence flow unless
+ * - the path visits the inclusive gateway or
+ * - the path visits a node that has a directed path to a non-empty incoming sequence
+ * flow of the inclusive gateway.
+ */
+ protected boolean isComplete(ExecutionImpl incomingExecution) {
+ String currentActivityId = incomingExecution.getActivityName(); // id is stored in the name attribute
+ Collection<ExecutionImpl> allExecutions = incomingExecution.getProcessInstance().getExecutions();
+ BpmnProcessDefinition processDefinition = (BpmnProcessDefinition) incomingExecution.getProcessDefinition();
+
+ for (ExecutionImpl execution : allExecutions) {
+ String activityId = execution.getActivityName(); // id is stored in the name attribute
+ if (activityId != null && !currentActivityId.equals(activityId)) {
+ if (processDefinition.isReachable(activityId, currentActivityId)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ protected List<ExecutionImpl> getJoinedExecutions(ExecutionImpl concurrentRoot, Activity activity) {
+ List<ExecutionImpl> joinedExecutions = new ArrayList<ExecutionImpl>();
+ List<ExecutionImpl> concurrentExecutions = (List<ExecutionImpl>)concurrentRoot.getExecutions();
+ for (ExecutionImpl concurrentExecution: (List<ExecutionImpl>)concurrentExecutions) {
+ if ( (Execution.STATE_INACTIVE_JOIN.equals(concurrentExecution.getState()))
+ && (concurrentExecution.getActivity()==activity)
+ ) {
+ joinedExecutions.add(concurrentExecution);
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Found " + joinedExecutions.size() + " executions currently waiting at the gateway");
+ }
+
+ return joinedExecutions;
+ }
+
+ protected void endJoinedExecutions(List<ExecutionImpl> joinedExecutions) {
+ for (ExecutionImpl joinedExecution: joinedExecutions) {
+ joinedExecution.end();
+ }
+ }
+
+
}
Modified: jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/ParallelGatewayActivity.java
===================================================================
--- jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/ParallelGatewayActivity.java 2010-01-15 14:04:04 UTC (rev 6083)
+++ jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/flownodes/ParallelGatewayActivity.java 2010-01-16 00:21:04 UTC (rev 6084)
@@ -44,7 +44,7 @@
private static final long serialVersionUID = 1L;
- LockMode lockMode = LockMode.UPGRADE;
+ protected LockMode lockMode = LockMode.UPGRADE;
public void execute(ActivityExecution execution) {
execute((ExecutionImpl) execution);
@@ -54,31 +54,38 @@
int nrOfIncoming = execution.getActivity().getIncomingTransitions().size();
if (nrOfIncoming == 1) { // no join behaviour needed, save some time and do a fork immediately
+
if (LOG.isDebugEnabled()) {
LOG.debug("Only one incoming sequence flow found. Executing fork logic.");
}
fork(execution);
- } else { // Join behaviour needed
+ } else {
+
if (LOG.isDebugEnabled()) {
LOG.debug("Multiple incoming sequence flow found. Executing join logic.");
}
- join(execution);
+ boolean allExecutionsArrived = handleIncomingExecution(execution);
// After executing the join functionality, it could be that all executions have arrived
- // at the gateway. In that case, the gateway can be left using the fork functionality.
- proceedIfPossible(execution);
+ // at the gateway. In that case, the gateway can be left.
+ if (allExecutionsArrived) {
+ ExecutionImpl outgoingExecution = join(execution);
+ fork(outgoingExecution);
+ }
}
-
-
}
protected void fork(ExecutionImpl execution) {
proceed(execution, findOutgoingSequenceFlow(execution, CONDITIONS_IGNORED));
}
- protected void join(ExecutionImpl execution) {
+ /**
+ * Joins the incoming executions.
+ * Returns true if all executions have reached the gateway.
+ */
+ protected boolean handleIncomingExecution(ExecutionImpl execution) {
if (Execution.STATE_ACTIVE_CONCURRENT.equals(execution.getState())) {
// force version increment in the parent execution
@@ -87,36 +94,46 @@
execution.setState(Execution.STATE_INACTIVE_JOIN);
execution.waitForSignal();
+
+ return isComplete(execution);
} else {
throw new JbpmException("invalid execution state: " + execution.getState());
}
}
- protected void proceedIfPossible(ExecutionImpl execution) {
+ /**
+ * Joins all the incoming executions currently waiting at the gateway.
+ *
+ * @return An execution that can be used to leave the gateway (one outgoing sequence flow)
+ * or to create child executions on (fork behaviour when multiple outgoing sequence flow).
+ */
+ protected ExecutionImpl join(ExecutionImpl execution) {
Activity activity = execution.getActivity();
ExecutionImpl concurrentRoot = execution.getParent();
List<ExecutionImpl> joinedExecutions = getJoinedExecutions(concurrentRoot, activity);
- if (isComplete(joinedExecutions, activity)) {
+ endJoinedExecutions(joinedExecutions);
- endJoinedExecutions(joinedExecutions);
+ ExecutionImpl outgoingExecution = null;
+ if (concurrentRoot.getExecutions().size() == 0) {
+ outgoingExecution = concurrentRoot;
+ outgoingExecution.setState(Execution.STATE_ACTIVE_ROOT);
+ } else {
+ outgoingExecution = concurrentRoot.createExecution();
+ outgoingExecution.setState(Execution.STATE_ACTIVE_CONCURRENT);
+ }
- ExecutionImpl outgoingExecution = null;
- if (concurrentRoot.getExecutions().size() == 0) {
- outgoingExecution = concurrentRoot;
- outgoingExecution.setState(Execution.STATE_ACTIVE_ROOT);
- } else {
- outgoingExecution = concurrentRoot.createExecution();
- outgoingExecution.setState(Execution.STATE_ACTIVE_CONCURRENT);
- }
-
- outgoingExecution.setActivity(activity);
- fork(outgoingExecution);
- }
+ outgoingExecution.setActivity(activity);
+ return outgoingExecution;
}
- protected boolean isComplete(List<ExecutionImpl> joinedExecutions, Activity activity) {
+ protected boolean isComplete(ExecutionImpl execution) {
+
+ Activity activity = execution.getActivity();
+ ExecutionImpl concurrentRoot = execution.getParent();
+ List<ExecutionImpl> joinedExecutions = getJoinedExecutions(concurrentRoot, activity);
+
boolean result = joinedExecutions.size() == activity.getIncomingTransitions().size();
if (LOG.isDebugEnabled()) {
LOG.debug("All incoming executions have arrived at the gateway: " + result);
Modified: jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/model/BpmnProcessDefinition.java
===================================================================
--- jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/model/BpmnProcessDefinition.java 2010-01-15 14:04:04 UTC (rev 6083)
+++ jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/model/BpmnProcessDefinition.java 2010-01-16 00:21:04 UTC (rev 6084)
@@ -23,12 +23,15 @@
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.jbpm.bpmn.common.Resource;
import org.jbpm.pvm.internal.model.ExecutionImpl;
import org.jbpm.pvm.internal.model.ProcessDefinitionImpl;
+import org.jbpm.pvm.internal.model.Transition;
import org.jbpm.pvm.internal.model.VariableDefinitionImpl;
import org.jbpm.pvm.internal.task.TaskDefinitionImpl;
import org.w3c.dom.Element;
@@ -37,13 +40,15 @@
private static final long serialVersionUID = 1L;
- Map<String, TaskDefinitionImpl> taskDefinitions = new HashMap<String, TaskDefinitionImpl>();
- List<VariableDefinitionImpl> processVariableDefinitions = new ArrayList<VariableDefinitionImpl>();
- Map<String, Element> messages = new HashMap<String, Element>();
- Map<String, Element> itemDefinitions = new HashMap<String, Element>();
- Map<String, Element> interfaces = new HashMap<String, Element>();
- Map<String, Element> operations = new HashMap<String, Element>();
- Map<String, Resource> resources = new HashMap<String, Resource>();
+ protected Map<String, TaskDefinitionImpl> taskDefinitions = new HashMap<String, TaskDefinitionImpl>();
+ protected List<VariableDefinitionImpl> processVariableDefinitions = new ArrayList<VariableDefinitionImpl>();
+ protected Map<String, Element> messages = new HashMap<String, Element>();
+ protected Map<String, Element> itemDefinitions = new HashMap<String, Element>();
+ protected Map<String, Element> interfaces = new HashMap<String, Element>();
+ protected Map<String, Element> operations = new HashMap<String, Element>();
+ protected Map<String, Resource> resources = new HashMap<String, Resource>();
+ protected Map<String, Transition> sequenceFlow = new HashMap<String, Transition>();
+ protected Map<String, Set<String>> sourceToTargetMapping = new HashMap<String, Set<String>>(); // convience mapping: sourceRefId to all targetRefs
protected ExecutionImpl newProcessInstance() {
return new ExecutionImpl();
@@ -112,4 +117,38 @@
this.itemDefinitions = itemDefinitions;
}
+ public void addSequenceFlow(String transitionId, Transition transition) {
+ this.sequenceFlow.put(transitionId, transition);
+
+ String source = transition.getSource().getName();
+ if (sourceToTargetMapping.get(source) == null) {
+ sourceToTargetMapping.put(source, new HashSet<String>());
+ }
+ sourceToTargetMapping.get(source).add(transition.getDestination().getName());
+ }
+
+ public Map<String, Set<String>> getSourceToTargetMapping() {
+ return sourceToTargetMapping;
+ }
+
+ public boolean isReachable(String srcActivityId , String dstActivityId) {
+ return isReachable(srcActivityId, dstActivityId, new HashSet<String>());
+ }
+
+ protected boolean isReachable(String srcActivityId , String dstActivityId, Set<String> alreadyVisited) {
+ if (srcActivityId.equals(dstActivityId)) {
+ return true;
+ } else {
+ alreadyVisited.add(srcActivityId);
+ for (String destinationId : sourceToTargetMapping.get(srcActivityId)) {
+ if (!alreadyVisited.contains(destinationId)) {
+ if (isReachable(destinationId, dstActivityId, alreadyVisited)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
}
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-15 14:04:04 UTC (rev 6083)
+++ jbpm4/trunk/modules/bpmn/src/main/java/org/jbpm/bpmn/parser/BpmnParser.java 2010-01-16 00:21:04 UTC (rev 6084)
@@ -32,11 +32,8 @@
import org.jbpm.api.activity.ActivityBehaviour;
import org.jbpm.bpmn.common.Resource;
import org.jbpm.bpmn.common.ResourceParameter;
-import org.jbpm.bpmn.flownodes.AbstractGatewayActivity;
import org.jbpm.bpmn.flownodes.BpmnBinding;
import org.jbpm.bpmn.flownodes.DatabasedGatewayActivity;
-import org.jbpm.bpmn.flownodes.ExclusiveGatewayActivity;
-import org.jbpm.bpmn.flownodes.InclusiveGatewayActivity;
import org.jbpm.bpmn.model.BpmnProcessDefinition;
import org.jbpm.internal.log.Log;
import org.jbpm.pvm.internal.model.ActivityImpl;
@@ -118,7 +115,6 @@
processDefinition.setDescription(description);
}
- // TODO: should be done in a different way? On a different level?
parseResources((Element)processElement.getParentNode(), parse, processDefinition);
parseInterfaces((Element)processElement.getParentNode(), parse, processDefinition);
@@ -135,7 +131,7 @@
parseActivities(processElement, parse, processDefinition);
// bind activities to their destinations
- parseSequenceFlows(processElement, parse, processDefinition);
+ parseSequenceFlow(processElement, parse, processDefinition);
} finally {
parse.contextStackPop();
@@ -233,7 +229,7 @@
}
}
- public void parseSequenceFlows(Element element, Parse parse, CompositeElementImpl compositeElement) {
+ public void parseSequenceFlow(Element element, Parse parse, BpmnProcessDefinition processDefinition) {
List<Element> transitionElements = XmlUtil.elements(element, "sequenceFlow");
for (Element transitionElement : transitionElements) {
String transitionName = XmlUtil.attribute(transitionElement, "name", false, parse);
@@ -246,19 +242,26 @@
}
Element conditionElement = XmlUtil.element(transitionElement, "conditionExpression");
- TransitionImpl transition = compositeElement.findActivity(sourceRef).createOutgoingTransition();
+ TransitionImpl transition = processDefinition.findActivity(sourceRef).createOutgoingTransition();
try {
// If something went wrong parsing the activity, there is no behaviour and an exception is thrown in .getBehaviour()
- ActivityBehaviour a = compositeElement.findActivity(sourceRef).getActivityBehaviour();
- if (a instanceof DatabasedGatewayActivity) {
- if (transitionId.equals(((DatabasedGatewayActivity) a).getDefault())) {
- compositeElement.findActivity(sourceRef).setDefaultOutgoingTransition(transition);
+ ActivityBehaviour behaviour = processDefinition.findActivity(sourceRef).getActivityBehaviour();
+ if (behaviour instanceof DatabasedGatewayActivity) {
+ DatabasedGatewayActivity databasedGatewayActivity = (DatabasedGatewayActivity) behaviour;
+ String defaultSeqFlow = databasedGatewayActivity.getDefault();
+ if (defaultSeqFlow != null) {
+ if (transitionId.equals(defaultSeqFlow)) {
+ processDefinition.findActivity(sourceRef).setDefaultOutgoingTransition(transition);
+ }
+ } else {
+ processDefinition.findActivity(sourceRef).setDefaultOutgoingTransition(null);
}
} else {
// Other flownodes do not have default sequenceFlows, so set it to null
- compositeElement.findActivity(sourceRef).setDefaultOutgoingTransition(null);
+ processDefinition.findActivity(sourceRef).setDefaultOutgoingTransition(null);
}
+
} catch (JbpmException je) {
// catch it and only re-throw if not this specific exception.
if (!je.getMessage().contains("no behaviour on")) {
@@ -280,15 +283,22 @@
transition.setCondition(expressionCondition);
} else {
- parse.addProblem("Type of the conditionExpression on sequenceFlow with id=" + transitionId + " is of onsupported type 'bpmn:tExpression'",
- transitionElement);
+ parse.addProblem("Type of the conditionExpression on sequenceFlow with id=" +
+ transitionId + " is of onsupported type 'bpmn:tExpression'", transitionElement);
}
}
- compositeElement.findActivity(targetRef).addIncomingTransition(transition);
+ ActivityImpl activity = processDefinition.findActivity(targetRef);
+ if (activity != null) {
+ activity.addIncomingTransition(transition);
+ } else {
+ parse.addProblem("TargetRef '" + targetRef + "' cannot be found");
+ }
transition.setName(transitionId);
transition.setDescription(transitionName);
+
+ processDefinition.addSequenceFlow(transitionId, transition);
}
}
public void parseDefinition(Element documentElement, Parse parse) {
Modified: jbpm4/trunk/modules/test-base/src/main/java/org/jbpm/test/assertion/CollectionAssertions.java
===================================================================
--- jbpm4/trunk/modules/test-base/src/main/java/org/jbpm/test/assertion/CollectionAssertions.java 2010-01-15 14:04:04 UTC (rev 6083)
+++ jbpm4/trunk/modules/test-base/src/main/java/org/jbpm/test/assertion/CollectionAssertions.java 2010-01-16 00:21:04 UTC (rev 6084)
@@ -24,6 +24,7 @@
*/
package org.jbpm.test.assertion;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
@@ -40,7 +41,7 @@
* Compares the elements of the two given collections.
* The order of elements is not checked.
*/
- public static <T> void assertElementsEqual(Collection<T> collection1, Collection<T> collection2) {
+ public static <T> void assertContainsSameElements(Collection<T> collection1, Collection<T> collection2) {
Assert.assertTrue("One of the given collections is null, while the other collection is not null",
(collection1 == null && collection2 == null)
@@ -62,6 +63,14 @@
}
+ /**
+ * Compares the elements of the two given collections.
+ * The order of elements is not checked.
+ */
+ public static <T> void assertContainsSameElements(Collection<T> collection1, T ... elements) {
+ assertContainsSameElements(collection1, Arrays.asList(elements));
+ }
+
@SuppressWarnings("unchecked")
private static String debugCollections(Collection ... collections) {
StringBuilder strb = new StringBuilder();
Modified: jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/bpmn/ParallelGatewayTest.java
===================================================================
--- jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/bpmn/ParallelGatewayTest.java 2010-01-15 14:04:04 UTC (rev 6083)
+++ jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/bpmn/ParallelGatewayTest.java 2010-01-16 00:21:04 UTC (rev 6084)
@@ -68,7 +68,7 @@
List<Task> allTasks = taskQuery.list();
assertEquals(2, allTasks.size());
- CollectionAssertions.assertElementsEqual(Arrays.asList("UserTaskLeg1", "UserTaskLeg2"),
+ CollectionAssertions.assertContainsSameElements(Arrays.asList("UserTaskLeg1", "UserTaskLeg2"),
Arrays.asList(allTasks.get(0).getActivityName(), allTasks.get(1).getActivityName()));
// specifying a transition is unnecessary, BPMN has outgoing AND semantic!
Added: jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/bpmn/test/gateway/InclusiveGatewayTest.java
===================================================================
--- jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/bpmn/test/gateway/InclusiveGatewayTest.java (rev 0)
+++ jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/bpmn/test/gateway/InclusiveGatewayTest.java 2010-01-16 00:21:04 UTC (rev 6084)
@@ -0,0 +1,159 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.bpmn.test.gateway;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jbpm.api.ProcessInstance;
+import org.jbpm.test.JbpmTestCase;
+import org.jbpm.test.assertion.CollectionAssertions;
+
+/**
+ * @author Joram Barrez
+ */
+public class InclusiveGatewayTest extends JbpmTestCase {
+
+ private static final String SIMPLE_SPLIT =
+ "<definitions xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>" +
+ " <process id='simpleInclusiveSplit' name='inclusiveSplit' >" +
+ " <startEvent id='theStart' />" +
+ " <sequenceFlow id='flow1' sourceRef='theStart' targetRef='inclusiveGateway' />" +
+ " <inclusiveGateway id='inclusiveGateway' />" +
+ " <sequenceFlow id='flow2' sourceRef='inclusiveGateway' targetRef='wait1' >" +
+ " <conditionExpression xsi:type='tFormalExpression'>${var > 5}</conditionExpression>" +
+ " </sequenceFlow>" +
+ " <sequenceFlow id='flow3' sourceRef='inclusiveGateway' targetRef='wait2' />" + // wait2 will always be reached since it has no condition
+ " <sequenceFlow id='flow4' sourceRef='inclusiveGateway' targetRef='wait3' >" +
+ " <conditionExpression xsi:type='tFormalExpression'>${var > 10}</conditionExpression>" +
+ " </sequenceFlow>" +
+ " <receiveTask id='wait1' />" +
+ " <sequenceFlow id='flow5' sourceRef='wait1' targetRef='theEnd' />" +
+ " <receiveTask id='wait2' />" +
+ " <sequenceFlow id='flow6' sourceRef='wait2' targetRef='theEnd' />" +
+ " <receiveTask id='wait3' />" +
+ " <sequenceFlow id='flow7' sourceRef='wait3' targetRef='theEnd' />" +
+ " <endEvent id='theEnd' >" +
+ " <terminateEventDefinition/>" +
+ " </endEvent>" +
+ " </process>" +
+ "</definitions>";
+
+ /*
+ * Copy of the SIMPLE_SPLIT_PROCESS, where the 'always' outgoing sequence flow is replaced
+ * by a default sequence flow.
+ */
+ private static final String SIMPLE_SPLIT_WITH_DEFAULT =
+ "<definitions xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>" +
+ " <process id='simpleInclusiveSplitWithDefault' name='inclusiveSplitWithDefault' >" +
+ " <startEvent id='theStart' />" +
+ " <sequenceFlow id='flow1' sourceRef='theStart' targetRef='inclusiveGateway' />" +
+ " <inclusiveGateway id='inclusiveGateway' default='flow3' />" +
+ " <sequenceFlow id='flow2' sourceRef='inclusiveGateway' targetRef='wait1' >" +
+ " <conditionExpression xsi:type='tFormalExpression'>${var > 5}</conditionExpression>" +
+ " </sequenceFlow>" +
+ " <sequenceFlow id='flow3' sourceRef='inclusiveGateway' targetRef='wait2' />" +
+ " <sequenceFlow id='flow4' sourceRef='inclusiveGateway' targetRef='wait3' >" +
+ " <conditionExpression xsi:type='tFormalExpression'>${var > 10}</conditionExpression>" +
+ " </sequenceFlow>" +
+ " <receiveTask id='wait1' />" +
+ " <sequenceFlow id='flow5' sourceRef='wait1' targetRef='theEnd' />" +
+ " <receiveTask id='wait2' />" +
+ " <sequenceFlow id='flow6' sourceRef='wait2' targetRef='theEnd' />" +
+ " <receiveTask id='wait3' />" +
+ " <sequenceFlow id='flow7' sourceRef='wait3' targetRef='theEnd' />" +
+ " <endEvent id='theEnd' >" +
+ " <terminateEventDefinition/>" +
+ " </endEvent>" +
+ " </process>" +
+ "</definitions>";
+
+ private static final String SIMPLE_SPLIT_AND_MERGE =
+ "<definitions xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>" +
+ " <process id='simpleSplitAndMerge' name='inclusiveSplitAndMerge' >" +
+ " <startEvent id='theStart' />" +
+ " <sequenceFlow id='flow1' sourceRef='theStart' targetRef='inclusiveSplit' />" +
+ " <inclusiveGateway id='inclusiveSplit' default='flow3' />" +
+ " <sequenceFlow id='flow2' sourceRef='inclusiveSplit' targetRef='wait1' >" +
+ " <conditionExpression xsi:type='tFormalExpression'>${var > 5}</conditionExpression>" +
+ " </sequenceFlow>" +
+ " <sequenceFlow id='flow3' sourceRef='inclusiveSplit' targetRef='wait2' />" +
+ " <sequenceFlow id='flow4' sourceRef='inclusiveSplit' targetRef='wait3' >" +
+ " <conditionExpression xsi:type='tFormalExpression'>${var > 10}</conditionExpression>" +
+ " </sequenceFlow>" +
+ " <receiveTask id='wait1' />" +
+ " <sequenceFlow id='flow5' sourceRef='wait1' targetRef='inclusiveJoin' />" +
+ " <receiveTask id='wait2' />" +
+ " <sequenceFlow id='flow6' sourceRef='wait2' targetRef='inclusiveJoin' />" +
+ " <receiveTask id='wait3' />" +
+ " <sequenceFlow id='flow7' sourceRef='wait3' targetRef='inclusiveJoin' />" +
+ " <inclusiveGateway id='inclusiveJoin' />" +
+ " <sequenceFlow id='flow8' sourceRef='inclusiveJoin' targetRef='theEnd' />" +
+ " <endEvent id='theEnd' />" +
+ " </process>" +
+ "</definitions>";
+
+ public void testSimpleSplit() {
+ deployBpmn2XmlString(SIMPLE_SPLIT);
+
+ // A var value < 5 will trigger all outgoing sequence flow
+ startAndVerifySimpleSplitProcess("simpleInclusiveSplit", 15, "wait1", "wait2", "wait3");
+
+ // A var value 0 < x < 10 will trigger only two sequence flow
+ startAndVerifySimpleSplitProcess("simpleInclusiveSplit", 7, "wait1", "wait2");
+
+ // A var value < 5 trigger only one sequence flow (the one without a condition)
+ startAndVerifySimpleSplitProcess("simpleInclusiveSplit", 3, "wait2");
+ }
+
+ public void testSimpleSplitWithDefault() {
+ deployBpmn2XmlString(SIMPLE_SPLIT_WITH_DEFAULT);
+
+ // A var value < 5 will trigger all outgoing sequence flow, but not the default one
+ startAndVerifySimpleSplitProcess("simpleInclusiveSplitWithDefault", 15, "wait1", "wait3");
+
+ // A var value 0 < x < 10 will trigger only one sequence flow
+ startAndVerifySimpleSplitProcess("simpleInclusiveSplitWithDefault", 7, "wait1");
+
+ // A var value < 5 trigger only one sequence flow (the default one)
+ startAndVerifySimpleSplitProcess("simpleInclusiveSplitWithDefault", 3, "wait2");
+ }
+
+ public void testSimpleSplitAndMerge() {
+ deployBpmn2XmlString(SIMPLE_SPLIT_AND_MERGE);
+
+ ProcessInstance pi = startAndVerifySimpleSplitProcess("simpleSplitAndMerge", 17, "wait1", "wait3");
+ executionService.signalExecutionById(pi.findActiveExecutionIn("wait1").getId());
+ assertProcessInstanceActive(pi);
+ executionService.signalExecutionById(pi.findActiveExecutionIn("wait3").getId());
+ assertProcessInstanceEnded(pi);
+ }
+
+ private ProcessInstance startAndVerifySimpleSplitProcess(String processKey, Integer varValue, String ... expectedActivities) {
+ Map<String, Object> vars = new HashMap<String, Object>();
+ vars.put("var", varValue);
+ ProcessInstance pi = executionService.startProcessInstanceByKey(processKey, vars);
+ CollectionAssertions.assertContainsSameElements(pi.findActiveActivityNames(), expectedActivities);
+ return pi;
+ }
+
+}
Modified: jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/bpmn/test/gateway/ParallelGatewayMergeTest.java
===================================================================
--- jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/bpmn/test/gateway/ParallelGatewayMergeTest.java 2010-01-15 14:04:04 UTC (rev 6083)
+++ jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/bpmn/test/gateway/ParallelGatewayMergeTest.java 2010-01-16 00:21:04 UTC (rev 6084)
@@ -113,14 +113,14 @@
deployBpmn2XmlString(TEST_SIMPLE_MERGE_PROCESS);
ProcessInstance pi = executionService.startProcessInstanceByKey("simpleMerge");
pi.findActiveActivityNames();
- CollectionAssertions.assertElementsEqual(pi.findActiveActivityNames(), Arrays.asList("wait1", "wait2"));
+ CollectionAssertions.assertContainsSameElements(pi.findActiveActivityNames(), Arrays.asList("wait1", "wait2"));
}
public void testNestedParallelMerge() {
deployBpmn2XmlString(TEST_NESTED_MERGE_PROCESS);
ProcessInstance pi = executionService.startProcessInstanceByKey("nestedMerge");
- CollectionAssertions.assertElementsEqual(pi.findActiveActivityNames(), Arrays.asList("wait1", "wait2"));
+ CollectionAssertions.assertContainsSameElements(pi.findActiveActivityNames(), Arrays.asList("wait1", "wait2"));
executionService.signalExecutionById(pi.findActiveExecutionIn("wait1").getId());
executionService.signalExecutionById(pi.findActiveExecutionIn("wait2").getId());
@@ -131,7 +131,7 @@
deployBpmn2XmlString(TEST_NESTED_MERGE_PROCESS_2);
ProcessInstance pi = executionService.startProcessInstanceByKey("nestedMerge2");
- CollectionAssertions.assertElementsEqual(pi.findActiveActivityNames(), Arrays.asList("wait"));
+ CollectionAssertions.assertContainsSameElements(pi.findActiveActivityNames(), Arrays.asList("wait"));
executionService.signalExecutionById(pi.findActiveExecutionIn("wait").getId());
assertProcessInstanceEnded(pi);
Modified: jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/test/activity/forkjoin/MultipleForksTest.java
===================================================================
--- jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/test/activity/forkjoin/MultipleForksTest.java 2010-01-15 14:04:04 UTC (rev 6083)
+++ jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/test/activity/forkjoin/MultipleForksTest.java 2010-01-16 00:21:04 UTC (rev 6084)
@@ -162,7 +162,7 @@
"</process>");
ProcessInstance pi = executionService.startProcessInstanceByKey("nestedForks");
- CollectionAssertions.assertElementsEqual(pi.findActiveActivityNames(), Arrays.asList("wait"));
+ CollectionAssertions.assertContainsSameElements(pi.findActiveActivityNames(), Arrays.asList("wait"));
executionService.signalExecutionById(pi.findActiveExecutionIn("wait").getId());
assertProcessInstanceEnded(pi);
}
More information about the jbpm-commits
mailing list