[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