Bug 975005 - Incorrect execution of fork/join after async node
Incorrect execution of fork/join after async node
Status: VERIFIED
Product: JBoss Enterprise SOA Platform 5
Classification: JBoss
Component: JBPM - standalone, JBPM - within SOA (Show other bugs)
5.3.1
Unspecified Unspecified
high Severity high
: GA
: ---
Assigned To: Shelly McGowan
:
Depends On:
Blocks: 986007
  Show dependency treegraph
 
Reported: 2013-06-17 07:10 EDT by Martin Weiler
Modified: 2016-09-20 01:09 EDT (History)
4 users (show)

See Also:
Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of:
Environment:
Last Closed:
Type: Bug
Regression: ---
Mount Type: ---
Documentation: ---
CRM:
Verified Versions:
Category: ---
oVirt Team: ---
RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: ---


Attachments (Terms of Use)


External Trackers
Tracker ID Priority Status Summary Last Updated
JBoss Issue Tracker JBPM-4030 Major Resolved Incorrect execution of fork/join after async node 2014-03-05 11:52:27 EST

  None (edit)
Description Martin Weiler 2013-06-17 07:10:56 EDT
Description of problem:
Platform issue for JBPM-4030
Comment 1 JBoss JIRA Server 2013-06-17 11:20:49 EDT
Martin Weiler <mweiler@redhat.com> made a comment on jira JBPM-4030

Attaching a test process to reproduce the issue. Note that the end state is reached twice, and that inside the fork/join the parent/child token relationship is empty.
Comment 2 JBoss JIRA Server 2013-06-18 10:24:39 EDT
Martin Weiler <mweiler@redhat.com> made a comment on jira JBPM-4030

Proposal to fix this issue:

{code}
diff --git a/core/src/main/java/org/jbpm/graph/node/Join.java b/core/src/main/java/org/jbpm/graph/node/Join.java
index bcff33a..9260e89 100644
--- a/core/src/main/java/org/jbpm/graph/node/Join.java
+++ b/core/src/main/java/org/jbpm/graph/node/Join.java
@@ -129,6 +129,10 @@ public class Join extends Node {
     }
 
     Token parentToken = arrivingToken.getParent();
+    // JBPM-4030 refresh parent token if necessary
+    if(parentToken.getChildren().size()==0) {    
+        executionContext.getJbpmContext().getSession().refresh(parentToken);
+    }
     boolean reactivateParent;
     // if this is a discriminator
     if (isDiscriminator) {
@@ -173,7 +177,10 @@ public class Join extends Node {
         childToken.setAbleToReactivateParent(false);
       }
       // unlock parent token
-      parentToken.unlock(parentToken.getNode().toString());
+      // JBPM-4030 check if parentToken can be unlocked
+      if(parentToken.isLocked() && parentToken.getLockOwner().equals(parentToken.getNode().toString())) {
+          parentToken.unlock(parentToken.getNode().toString());
+      }
       // leave the join node
       leave(new ExecutionContext(parentToken));
     }
{code}
Comment 3 JBoss JIRA Server 2013-06-24 15:47:26 EDT
Marco Rietveld <marco.rietveld@redhat.com> made a comment on jira JBPM-4030

Martin, this is fantastic work! Thanks very much.
Comment 4 JBoss JIRA Server 2013-06-25 05:35:01 EDT
Marco Rietveld <marco.rietveld@redhat.com> made a comment on jira JBPM-4030

This is a note to myself for future reference: 

One of the main causes of this problem is the following code in the Fork class: 

{code:java}
    // phase two: create child token for each selected transition
    Map childTokens = new HashMap();
    for (Iterator iter = transitionNames.iterator(); iter.hasNext();) {
      String transitionName = (String) iter.next();
      Token childToken = createForkedToken(token, transitionName);
      childTokens.put(transitionName, childToken);
    }
{code}

Specifically, the {code:java}Map childTokens = new HashMap();{code} line is a lot more dangerous than it looks. 

*Why?* Because jBPM 3 uses the Hibernate 3 2nd level cache, which means that many of the objects referred to in the code, are actually hibernate-proxies of the actual objects. You'll notice that the method {{createForkedToken()}} is called, and your suspicion that creates the parent-child token relationship will be correct -- but only partially. 

*Here's what happens:* {{createdForkedToken()}} eventually calls the {{Token(Token parent, String name)}} constructor. And that constructor contains the following code: 
{code:java}
    this.parent = parent;
    parent.addChild(this);
{code}

Great! *NO..* Why? Because the parent object referred to here is the _actual_ object, not the hibernate-proxied Token object. That means that when we return to the original {{execute(...)}} in the {{Fork}} class, the hibernate-proxied (parent) Token object will still have *no* children! Blech.. 

And we return to why {{Map childTokens = new HashMap();}} is so dangerous. It should simply be this: {code:java}Map childTokens = token.getChildren();{code}. That way, we add the children to the hibernate-proxied Token object map. If you've been paying attention, you'll notice that we're now adding this (parent-child) relationship twice: once in the {{Fork.execute(...)}} method (with the above line), and once in the {{createForkedToken(...)}} method. This is okay for 2 reasons: 

# We're adding the same relationship: in both cases, the key is the transition name, and the value is the (same) child token reference.
# Technically, we're adding this relationship to 2 different maps: the first map is from the hibernate-proxied parent Token, and the second map is from the (real) parent Token object.
Comment 5 JBoss JIRA Server 2013-06-25 10:53:29 EDT
Marco Rietveld <marco.rietveld@redhat.com> made a comment on jira JBPM-4030

Wow, there's an even simpler solution: simply initalize the {{Token.children}} (Map) field with a {{new HashMap()}} value so that the hibernate-proxied object also contains that field. LOL.
Comment 6 JBoss JIRA Server 2013-06-28 05:14:53 EDT
Marco Rietveld <marco.rietveld@redhat.com> updated the status of jira JBPM-4030 to Resolved

Note You need to log in before you can comment on or make changes to this bug.